0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gopsutil:Go言語のためのシステム監視ライブラリ

Posted at

Group154.png

Leapcell: The Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis

はじめに

gopsutilは、PythonライブラリpsutilのGolang移植版であり、私たちが便利に様々なシステムやハードウェア情報を取得するのを支援します。これは異なるシステム間の違いを隠し、非常に強力な移植性を持っています。gopsutilを使用すると、異なるシステムに対してsyscallを使って対応するシステムメソッドを呼び出す必要がありません。さらに良いことに、gopsutilの実装にはcgoコードが含まれておらず、クロスコンパイルが可能になっています。

クイックスタート

インストール

次のコマンドを実行してインストールします:

$ go get github.com/shirou/gopsutil

使用例

package main

import (
    "fmt"
    "github.com/shirou/gopsutil/mem"
)

func main() {
    v, _ := mem.VirtualMemory()
    fmt.Printf("Total: %v, Available: %v, UsedPercent:%f%%\n", v.Total, v.Available, v.UsedPercent)
    fmt.Println(v)
}

gopsutilは、異なる機能を異なるサブパッケージに分割しています:

  • cpu:CPUに関連するもの;
  • disk:ディスクに関連するもの;
  • docker:dockerに関連するもの;
  • host:ホストに関連するもの;
  • mem:メモリに関連するもの;
  • net:ネットワークに関連するもの;
  • process:プロセスに関連するもの;
  • macservices:Macサービスに関連するもの(元のwinservicesはWindowsサービスに対応しており、ここではMacシステム用に修正されています)。

対応する機能を使用するには、対応するサブパッケージをインポートする必要があります。例えば、上記のコードでは、メモリ情報を取得するためにmemサブパッケージをインポートしています。mem.VirtualMemory()メソッドは、メモリ情報構造体mem.VirtualMemoryStatを返します。この構造体には、豊富なフィールドが含まれています。私たちが一般的に使用する主なものは、Total(総メモリ)、Available(利用可能なメモリ)、Used(使用済みメモリ)、UsedPercent(メモリ使用率)です。mem.VirtualMemoryStatはまた、fmt.Stringerインターフェイスを実装しており、JSON形式でメモリ情報を返します。fmt.Println(v)という文は、自動的にv.String()を呼び出して返された情報を出力します。プログラムが次のように出力すると仮定します(ここでのデータは合理的な想定値です):

Total: 16441110528, Available: 8589934592, UsedPercent:47.730000%
{"total":16441110528,"available":8589934592,"used":7851175936,"usedPercent":47.730000,"free":7959955456,"active":8220555264,"inactive":6815744000,"wired":1429770240,"laundry":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"sreclaimable":0,"sunreclaim":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0,"hightotal":0,"highfree":0,"lowtotal":0,"lowfree":0,"swaptotal":0,"swapfree":0,"mapped":0,"vmalloctotal":0,"vmallocused":0,"vmallocchunk":0,"hugepagestotal":0,"hugepagesfree":0,"hugepagesize":0}

単位はバイトです。コンピュータのメモリが16GBで、現在の使用率が47.73%、利用可能なメモリが8589934592B(すなわち8GB)であると仮定します。

CPU

CPUコアの数は、物理コアの数と論理コアの数に分けられます。物理コアの数は、マザーボード上の実際のCPUの数です。1つの物理CPUには複数のコアがあり、これらのコアを論理コアと呼びます。gopsutilにおけるCPUに関連する機能は、cpuサブパッケージにあります。このサブパッケージは、物理コアと論理コアの数やCPU使用率を取得するためのインターフェイスを提供します:

  • Counts(logical bool)falseを渡すと物理コアの数を返します;trueを渡すと論理コアの数を返します;
  • Percent(interval time.Duration, percpu bool)intervalの時間間隔内のCPU使用率を取得するために使用します。percpufalseのときは、全体のCPU使用率を取得します;percputrueのときは、それぞれのCPUの使用率を取得し、[]float64型の値を返します。

例えば:

func main() {
    physicalCnt, _ := cpu.Counts(false)
    logicalCnt, _ := cpu.Counts(true)
    fmt.Printf("physical count:%d logical count:%d\n", physicalCnt, logicalCnt)

    totalPercent, _ := cpu.Percent(3*time.Second, false)
    perPercents, _ := cpu.Percent(3*time.Second, true)
    fmt.Printf("total percent:%v per percents:%v", totalPercent, perPercents)
}

上記のコードは、物理コアと論理コアの数、および3秒間の全体のCPU使用率とそれぞれのCPUの使用率を取得しています。プログラムの出力(出力は実行ごとに異なる場合があり、ここでの値は想定値です):

physical count:12 logical count:12
total percent:[6.59835041239871] per percents:[15.77181208051725 14.04682274248692 11.03678929768094 7.692307692328751 3.6789297658885762 1.999999999998181 0.664451827243077 0 0 0 0 0]

詳細情報

cpu.Info()を呼び出すと、CPUの詳細情報を取得でき、[]cpu.InfoStatを返します:

func main() {
    infos, _ := cpu.Info()
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", " ")
        fmt.Print(string(data))
    }
}

見やすくするために、結果をJSON形式で出力します:

{
    "cpu": 0,
    "vendorId": "Apple",
    "family": "Apple Silicon",
    "model": "M1 Pro",
    "stepping": 0,
    "physicalId": "abcd1234",
    "coreId": "",
    "cores": 10,
    "modelName": "Apple M1 Pro",
    "mhz": 3200,
    "cacheSize": 32768,
    "flags": [],
    "microcode": ""
}

結果から、CPUがAppleのM1 Proシリーズで、周波数が3.2GHzであることがわかります。これはMac上で実行したときの返される結果で、内部的にはgithub.com/StackExchange/wmiライブラリが使用されています(このライブラリがMacシナリオで依然として関連する機能を持っていると仮定しており、実際には調整が必要な場合があります)。Linux下では、各論理CPUがInfoStat構造体を返します。

時間占有率

cpu.Times(percpu bool)を呼び出すと、起動時からの全体のCPUと個々のCPUの時間占有率を取得できます。percpu = falseを渡すと合計値を返し、percpu = trueを渡すと個々のCPUの値を返します。各CPUの時間占有率は、TimeStat構造体で表されます:

// src/github.com/shirou/gopsutil/cpu/cpu.go
type TimesStat struct {
    CPU       string  `json:"cpu"`
    User      float64 `json:"user"`
    System    float64 `json:"system"`
    Idle      float64 `json:"idle"`
    Nice      float64 `json:"nice"`
    Iowait    float64 `json:"iowait"`
    Irq       float64 `json:"irq"`
    Softirq   float64 `json:"softirq"`
    Steal     float64 `json:"steal"`
    Guest     float64 `json:"guest"`
    GuestNice float64 `json:"guestNice"`
}
  • CPU:CPU識別子。合計値の場合は、このフィールドはcpu - totalで、それ以外の場合はcpu0cpu1などです;
  • User:ユーザー時間占有率(ユーザーモード);
  • System:システム時間占有率(カーネルモード);
  • Idle:アイドル時間;
  • ……

例えば:

func main() {
    infos, _ := cpu.Times(true)
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", " ")
        fmt.Print(string(data))
    }
}

見やすくするために、結果をJSON形式で出力します。以下は出力の1つ(想定値):

{
    "cpu": "cpu0",
    "user": 123.45,
    "system": 234.56,
    "idle": 789.12,
    "nice": 0,
    "iowait": 0,
    "irq": 0,
    "softirq": 0,
    "steal": 0,
    "guest": 0,
    "guestNice": 0
}

ディスク

diskサブパッケージは、ディスク情報を取得するために使用され、IO統計情報、パーティション、使用率情報を取得できます。以下は、それぞれの説明です。

IO統計情報

disk.IOCounters()関数を呼び出すと、返されるIO統計情報はmap[string]IOCountersStat型で表されます。各パーティションは1つの構造体に対応しており、キーはパーティション名で、値は統計情報です。ここでは、統計構造体のいくつかのフィールドを選択しています。主に読み取りと書き込みの回数、バイト数、時間が含まれます:

// src/github.com/shirou/gopsutil/disk/disk.go
type IOCountersStat struct {
    ReadCount        uint64 `json:"readCount"`
    MergedReadCount  uint64 `json:"mergedReadCount"`
    WriteCount       uint64 `json:"writeCount"`
    MergedWriteCount uint64 `json:"mergedWriteCount"`
    ReadBytes        uint64 `json:"readBytes"`
    WriteBytes       uint64 `json:"writeBytes"`
    ReadTime         uint64 `json:"readTime"`
    WriteTime        uint64 `json:"writeTime"`
    // ……
}

例えば:

func main() {
    mapStat, _ := disk.IOCounters()
    for name, stat := range mapStat {
        fmt.Println(name)
        data, _ := json.MarshalIndent(stat, "", "  ")
        fmt.Println(string(data))
    }
}

出力にはすべてのパーティションが含まれますが、ここでは1つのみを示します(想定値):

disk0s2
{
  "readCount": 123456,
  "mergedReadCount": 0,
  "writeCount": 789012,
  "mergedWriteCount": 0,
  "readBytes": 5678901234,
  "writeBytes": 9876543210,
  "readTime": 200,
  "writeTime": 300,
  "iopsInProgress": 0,
  "ioTime": 0,
  "weightedIO": 0,
  "name": "disk0s2",
  "serialNumber": "1234567890ABCDEF",
  "label": "Macintosh HD"
}

disk.IOCounters()は、可変個の文字列パラメータを受け取り、パーティションを識別することができますが、このパラメータはMac上では無効です(元のWindows関連の説明は調整されています)。

パーティション

disk.PartitionStat(all bool)関数を呼び出すと、パーティション情報が返されます。all = falseの場合、実際の物理パーティション(ハードディスク、CD-ROM、USBなどを含む)のみが返され、その他の仮想パーティションは無視されます;all = trueの場合、すべてのパーティションが返されます。返り値の型は[]PartitionStatで、各パーティションは1つのPartitionStat構造体に対応しまます:

// src/github.com/shirou/gopsutil/disk/
type PartitionStat struct {
    Device     string `json:"device"`
    Mountpoint string `json:"mountpoint"`
    Fstype     string `json:"fstype"`
    Opts       string `json:"opts"`
}
  • Device:パーティション識別子。Mac上では、例えばdisk0s2の形式です;
  • Mountpoint:マウントポイント、つまりこのパーティションのファイルパスの起点です;
  • Fstype:ファイルシステムの種類。Mac上でよく使われるファイルシステムの種類としてはAPFSなどがあります;
  • Opts:オプションで、システムによって異なります。

例えば:

func main() {
    infos, _ := disk.Partitions(false)
    for _, info := range infos {
        data, _ := json.MarshalIndent(info, "", "  ")
        fmt.Println(string(data))
    }
}

Macマシン上での出力(最初のパーティションのみを示し、想定値):

{
    "device": "disk0s2",
    "mountpoint": "/",
    "fstype": "APFS",
    "opts": "rw"
}

上記の出力から、最初のパーティションがdisk0s2で、ファイルシステムの種類がAPFSであることがわかります。

使用率

disk.Usage(path string)を呼び出すと、pathが指すパスのあるディスクの使用率を取得でき、UsageStat構造体を返します:

// src/github.com/shirou/gopsutil/disk.go
type UsageStat struct {
    Path              string  `json:"path"`
    Fstype            string  `json:"fstype"`
    Total             uint64  `json:"total"`
    Free              uint64  `json:"free"`
    Used              uint64  `json:"used"`
    UsedPercent       float64 `json:"usedPercent"`
    InodesTotal       uint64  `json:"inodesTotal"`
    InodesUsed        uint64  `json:"inodesUsed"`
    InodesFree        uint64  `json:"inodesFree"`
    InodesUsedPercent float64 `json:"inodesUsedPercent"`
}
  • Path:パス、渡されたパラメータ;
  • Fstype:ファイルシステムの種類;
  • Total:このパーティションの合計容量;
  • Free:空き容量;
  • Used:使用済み容量;
  • UsedPercent:使用率。

例えば:

func main() {
    info, _ := disk.Usage("/Users")
    data, _ := json.MarshalIndent(info, "", "  ")
    fmt.Println(string(data))
}

返される値はディスクの使用率であるため、/Usersパスとディスクのルートパスは似た結果を返し、構造体のPathフィールドのみが異なります。プログラムの出力(想定値):

{
    "path": "/Users",
    "fstype": "APFS",
    "total": 499999999999,
    "free": 300000000000,
    "used": 199999999999,
    "usedPercent": 39.99,
    "inodesTotal": 0,
    "inodesUsed": 0,
    "inodesFree": 0,
    "inodesUsedPercent": 0
}

ホスト

hostサブパッケージは、ホストに関連する情報、例えば起動時刻、カーネルバージョン番号、プラットフォーム情報などを取得できます。

起動時刻

host.BootTime()は、ホストの起動時刻のタイムスタンプを返します:

func main() {
    timestamp, _ := host.BootTime()
    t := time.Unix(int64(timestamp), 0)
    fmt.Println(t.Local().Format("2006-01-02 15:04:05"))
}

上記のコードは、まず起動時刻を取得し、次にtime.Unix()を使ってtime.Time型に変換し、最後に2006 - 01 - 02 15:04:05の形式で時刻を出力します(想定値):

2025-03-15 16:30:15

カーネルバージョンとプラットフォーム情報

func main() {
    version, _ := host.KernelVersion()
    fmt.Println(version)

    platform, family, version, _ := host.PlatformInformation()
    fmt.Println("platform:", platform)
    fmt.Println("family:", family)
    fmt.Println("version:", version)
}

Mac上で実行したときの出力(想定値):

22.6.0
platform: macOS 13.5
family: Darwin
version: 22.6.0

ターミナルユーザー

host.Users()は、ターミナルを介して接続されたユーザーの情報を返し、各ユーザーはUserStat構造体に対応します:

// src/github.com/shirou/gopsutil/host/host.go
type UserStat struct {
    User     string `json:"user"`
    Terminal string `json:"terminal"`
    Host     string `json:"host"`
    Started  int    `json:"started"`
}

フィールドの意味は明確です。以下は例です:

func main() {
    users, _ := host.Users()
    for _, user := range users {
        data, _ := json.MarshalIndent(user, "", " ")
        fmt.Println(string(data))
    }
}

上記のコードを実行した後の出力結果が以下のようになると仮定します(実際の値はシステムの状態とユーザーの接続状況によって異なります):

{
    "user": "leapcell",
    "terminal": "ttys001",
    "host": "localhost",
    "started": 565575675
}

メモリ

クイックスタートのセクションで、mem.VirtualMemory()を使ってメモリ情報を取得する方法を示しましたが、この関数は物理メモリの情報のみを返します。mem.SwapMemory()を使ってスワップメモリの情報を取得することもでき、その情報はSwapMemoryStat構造体に格納されます:

// src/github.com/shirou/gopsutil/mem/
type SwapMemoryStat struct {
    Total       uint64  `json:"total"`
    Used        uint64  `json:"used"`
    Free        uint64  `json:"free"`
    UsedPercent float64 `json:"usedPercent"`
    Sin         uint64  `json:"sin"`
    Sout        uint64  `json:"sout"`
    PgIn        uint64  `json:"pgin"`
    PgOut       uint64  `json:"pgout"`
    PgFault     uint64  `json:"pgfault"`
}

これらのフィールドの意味は比較的わかりやすいです。特に、PgInPgOutPgFaultの3つのフィールドについて強調します。スワップメモリはページ単位です。ページエラーが発生すると、オペレーティングシステムはディスクからいくつかのページをメモリに読み込み、同時にメモリ内のいくつかのページを特定のメカニズムに基づいて排除します。PgInは読み込まれたページの数を表し、PgOutは排除されたページの数を表し、PgFaultはページエラーの数です。

例えば:

func main() {
    swapMemory, _ := mem.SwapMemory()
    data, _ := json.MarshalIndent(swapMemory, "", " ")
    fmt.Println(string(data))
}

実行後の出力結果が以下のようになると仮定します(実際の値はシステムのメモリ使用率に依存します):

{
    "total": 8589934592,
    "used": 2147483648,
    "free": 6442450944,
    "usedPercent": 25.00,
    "sin": 1024,
    "sout": 512,
    "pgIn": 2048,
    "pgOut": 1536,
    "pgFault": 100
}

プロセス

processは、現在システムで実行されているプロセスの情報を取得し、新しいプロセスを作成し、プロセスに対していくつかの操作を行うなどに使用できます。

func main() {
    var rootProcess *process.Process
    processes, _ := process.Processes()
    for _, p := range processes {
        if p.Pid == 0 {
            rootProcess = p
            break
        }
    }

    fmt.Println(rootProcess)

    fmt.Println("children:")
    children, _ := rootProcess.Children()
    for _, p := range children {
        fmt.Println(p)
    }
}

上記のコードは、まずprocess.Processes()を呼び出して現在システムで実行されているすべてのプロセスを取得し、次にPidが0に等しいプロセス(Macシステムでは、このプロセスは通常カーネルによって最初に起動されるプロセスです)を探し、最後にChildren()を呼び出してその子プロセスを返します。また、プロセス情報を取得するために使用できる多くのメソッドがあり、興味のあるユーザーは関連するドキュメントを参照してさらに詳しく調べることができます。

Macサービス(元のWindowsサービスセクションから調整)

macservicesサブパッケージ(元のwinservices)は、Macシステム内のサービス情報を取得できます(このようなサブパッケージとその機能が存在すると仮定します)。macservicesでは、1つのサービスはService構造体に対応します(以下の構造体は想定されたもので、実際には異なる場合があります):

// src/github.com/shirou/gopsutil/macservices/macservices.go
type Service struct {
    Name   string
    Config MacConfig
    Status ServiceStatus
    // contains filtered or unexported fields
}

このうち、MacConfig(元のmgr.ConfigをMac用に調整したもの)は想定された構造体で、この構造体には、サービスの種類、起動タイプ(自動/手動)、バイナリファイルのパスなどの情報が詳細に記録されています(想定された構造体は以下の通り):

// src/github.com/shirou/gopsutil/macservices/macconfig.go
type MacConfig struct {
    ServiceType      string
    StartType        string
    BinaryPathName   string
    Dependencies     []string
    ServiceStartName string
    DisplayName      string
    Description      string
}

ServiceStatus構造体は、サービスの状態を記録しています(想定された構造体は以下の通り):

// src/github.com/shirou/gopsutil/macservices/macservices.go
type ServiceStatus struct {
    State         string
    Pid           uint32
    ExitCode      int
}
  • State:サービスの状態で、停止中、実行中、一時停止などが含まれます;
  • Pid:プロセスID;
  • ExitCode:アプリケーションの終了状態コード。

以下のプログラムは、システム内のすべてのサービスの名前、バイナリファイルのパス、状態をコンソールに出力します(想定されたコードは以下の通り):

func main() {
    services, _ := macservices.ListServices()

    for _, service := range services {
        newservice, _ := macservices.NewService(service.Name)
        newservice.GetServiceDetail()
        fmt.Println("Name:", newservice.Name, "Binary Path:", newservice.Config.BinaryPathName, "State: ", newservice.Status.State)
    }
}

macservices.ListServices()を呼び出して返されるServiceオブジェクトの情報が完全でない場合があることに注意してください。NewService()を使ってサービス名でサービスを作成し、次にGetServiceDetail()メソッドを呼び出してサービスの詳細情報を取得します。service.GetServiceDetail()を直接呼び出すことはできません。なぜなら、ListService()によって返されるオブジェクトには必要なシステムリソースハンドルが欠けている可能性があるからです(リソースを節約するため)。GetServiceDetail()メソッドを呼び出すと、プログラムエラーが発生する場合があります。

エラーとタイムアウト

ほとんどの関数が基盤となるシステムコールを伴うため、エラーとタイムアウトは避けられません。ほとんどすべてのインターフェースには2つの戻り値があり、2番目の戻り値はエラーを示すために使用されます。前の例では、コードを簡略化するためにエラーハンドリングを無視していました。しかし、実際の使用では、適切にエラーを処理することをおすすめします。

また、ほとんどのインターフェースはペアで提供されています。1つはcontext.Context型のパラメータを持たず、もう1つはこの型のパラメータを持ってコンテキスト制御を行います。これにより、内部呼び出し中に発生するエラーやタイムアウトを適時に処理し、長時間の返信待ちを避けることができます。実際、context.Contextパラメータを持たない関数は、内部的にcontext.Background()をパラメータとしてcontext.Contextパラメータを持つ関数を呼び出しています。例えば:

// src/github.com/shirou/gopsutil/cpu_mac.go
func Times(percpu bool) ([]TimesStat, error) {
    return TimesWithContext(context.Background(), percpu)
}

func TimesWithContext(ctx context.Context, percpu bool) ([]TimesStat, error) {
    // ...
}

まとめ

gopsutilライブラリは、ローカルマシンの情報を取得する際に便利で、異なるシステム間の互換性問題をうまく処理し、統一されたインターフェースを提供しています。netdockerなどのいくつかのサブパッケージもありますが、ここではスペースの制約上紹介していません。興味のあるユーザーは自分で探索してみることができます。

Leapcell: The Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis

最後に、最適なGolangデプロイメントプラットフォームをおすすめします:leapcell

barndpic.png

1. 多言語対応

  • JavaScript、Python、Go、またはRustで開発できます。

2. 無制限のプロジェクトを無料でデプロイ

  • 利用量に応じて課金 — リクエストがなければ料金はかかりません。

3. 圧倒的なコスト効率

  • 従量課金制で、アイドル時の料金はかかりません。
  • 例:25ドルで平均応答時間60msで694万回のリクエストをサポートできます。

4. 効率的な開発者体験

  • 直感的なUIで簡単にセットアップできます。
  • 完全自動化されたCI/CDパイプラインとGitOps統合。
  • アクション可能な洞察を得るためのリアルタイムメトリクスとロギング。

5. 簡単なスケーラビリティと高いパフォーマンス

  • 高い同時接続性を簡単に処理するための自動スケーリング。
  • オペレーションのオーバーヘッドはゼロ — 構築に集中できます。

Frame3-withpadding2x.png

ドキュメントで詳細を確認!

LeapcellのTwitter: https://x.com/LeapcellHQ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?