Sistem Kaynaklarını İzleme: Python psutil Kütüphanesi

Sistem yöneticiliğinde en can sıkıcı durumlardan biri, bir şeyler ters gittiğinde “ne zaman başladı?” sorusuna cevap verememektir. Sunucu yavaşlamış, disk dolmuş ya da bir process RAM’i yutmuş ama sen ancak kullanıcılar şikayet etmeye başladıktan sonra haberdar olmuşsundur. İşte bu yüzden kaynak izleme, reaktif değil proaktif bir yaklaşım gerektiriyor. Python’un psutil kütüphanesi bu noktada gerçekten hayat kurtaran bir araç haline geliyor.

psutil Nedir ve Neden Kullanmalıyız?

psutil (process and system utilities), Linux, Windows, macOS ve BSD sistemlerde çalışan, sistem kaynaklarını ve çalışan processleri sorgulamak için geliştirilmiş bir Python kütüphanesidir. top, htop, vmstat, netstat gibi araçların programatik karşılığı olarak düşünebilirsiniz.

Neden script yazmak yerine bu araçları doğrudan kullanmıyoruz? Çünkü:

  • Shell komutlarının çıktısını parse etmek hem kırılgan hem de platforma özgü bir iştir
  • psutil ile topladığınız veriyi doğrudan bir veritabanına yazabilir, alert gönderebilir ya da bir API’ye postlayabilirsiniz
  • Windows ve Linux’ta aynı kodu çalıştırmak istiyorsanız psutil bunu sorunsuz halleder
  • Çıktı zaten Python nesnesi olarak gelir, ek parse işlemi gerekmez

Kurulum tek satır:

pip install psutil

Bir sanal ortamda çalışıyorsanız:

python3 -m venv monitoring-env
source monitoring-env/bin/activate
pip install psutil

CPU Kullanımını İzlemek

En temel ihtiyaçtan başlayalım. CPU kullanımını çekerken dikkat edilmesi gereken önemli bir nokta var: cpu_percent() fonksiyonu ilk çağrıldığında 0.0 döner çünkü bir önceki ölçümle karşılaştırma yapacak referans noktası yoktur. Bu yüzden interval parametresi kullanmak ya da bir başlangıç çağrısı yapmak gerekir.

import psutil
import time

# Tüm CPU çekirdeklerinin kullanımını ayrı ayrı göster
def cpu_monitor(interval=1, count=5):
    print(f"CPU Çekirdek Sayısı (Fiziksel): {psutil.cpu_count(logical=False)}")
    print(f"CPU Çekirdek Sayısı (Mantıksal): {psutil.cpu_count(logical=True)}")
    print("-" * 40)

    for i in range(count):
        # interval parametresi iki ölçüm arasındaki süreyi belirler
        overall = psutil.cpu_percent(interval=interval)
        per_core = psutil.cpu_percent(interval=None, percpu=True)
        
        print(f"[{time.strftime('%H:%M:%S')}] Genel CPU: {overall}%")
        for idx, core in enumerate(per_core):
            print(f"  Core {idx}: {core}%")
        print()

cpu_monitor()

CPU frekans bilgisini de almak mümkün. Özellikle laptop veya güç tasarrufu modunda çalışan sistemlerde throttling olup olmadığını kontrol etmek için kullanışlıdır:

import psutil

freq = psutil.cpu_freq()
if freq:
    print(f"Mevcut Frekans: {freq.current:.0f} MHz")
    print(f"Minimum Frekans: {freq.min:.0f} MHz")
    print(f"Maksimum Frekans: {freq.max:.0f} MHz")

# CPU load average (sadece Unix sistemlerde)
try:
    load = psutil.getloadavg()
    print(f"Load Average (1/5/15 dk): {load[0]:.2f} / {load[1]:.2f} / {load[2]:.2f}")
except AttributeError:
    print("Load average Windows'ta desteklenmiyor.")

RAM ve Swap Kullanımı

Bellek sorunları genellikle CPU sorunlarından daha sinsi olur. Yavaş yavaş büyüyen bir memory leak, sistemi saatler içinde çökertebilir. virtual_memory() ve swap_memory() fonksiyonları bu noktada oldukça detaylı bilgi verir.

import psutil

def memory_report():
    vm = psutil.virtual_memory()
    swap = psutil.swap_memory()
    
    # Byte değerlerini okunabilir hale getir
    def to_gb(bytes_val):
        return bytes_val / (1024 ** 3)
    
    print("=== RAM Durumu ===")
    print(f"Toplam  : {to_gb(vm.total):.2f} GB")
    print(f"Kullanılan : {to_gb(vm.used):.2f} GB ({vm.percent}%)")
    print(f"Boş    : {to_gb(vm.available):.2f} GB")
    print(f"Buffer  : {to_gb(vm.buffers):.2f} GB")
    print(f"Cache   : {to_gb(vm.cached):.2f} GB")
    
    print("n=== Swap Durumu ===")
    print(f"Toplam  : {to_gb(swap.total):.2f} GB")
    print(f"Kullanılan : {to_gb(swap.used):.2f} GB ({swap.percent}%)")
    print(f"Boş    : {to_gb(swap.free):.2f} GB")
    
    # Uyarı mekanizması
    if vm.percent > 85:
        print("n[UYARI] RAM kullanımı %85 üzerinde!")
    if swap.percent > 50:
        print("[UYARI] Swap kullanımı %50 üzerinde, bellek baskısı var!")

memory_report()

Disk Kullanımını İzlemek

Disk dolma problemleri sysadminlerin gecelerini bölen klasik senaryolardan biridir. /var/log dizininin patlaması, büyük bir dump dosyasının beklenmedik bir yere yazılması… Hepsini erken yakalamak için disk izleme şart.

import psutil

def disk_report():
    print("=== Disk Partition Bilgileri ===n")
    
    partitions = psutil.disk_partitions()
    
    for partition in partitions:
        print(f"Cihaz    : {partition.device}")
        print(f"Bağlama Noktası: {partition.mountpoint}")
        print(f"Dosya Sistemi : {partition.fstype}")
        
        try:
            usage = psutil.disk_usage(partition.mountpoint)
            total_gb = usage.total / (1024 ** 3)
            used_gb = usage.used / (1024 ** 3)
            free_gb = usage.free / (1024 ** 3)
            
            print(f"Toplam   : {total_gb:.1f} GB")
            print(f"Kullanılan : {used_gb:.1f} GB")
            print(f"Boş    : {free_gb:.1f} GB")
            print(f"Doluluk  : {usage.percent}%")
            
            if usage.percent >= 90:
                print(f"[KRİTİK] {partition.mountpoint} dolmak üzere!")
            elif usage.percent >= 75:
                print(f"[UYARI] {partition.mountpoint} için yer açın.")
                
        except PermissionError:
            print("Erişim izni yok, atlanıyor.")
        
        print("-" * 35)

disk_report()

Disk I/O istatistiklerine de bakabiliriz. Özellikle yüksek I/O wait değerleri yaşandığında hangi diskin sorun çıkardığını bulmak için kullanışlıdır:

import psutil
import time

# İki ölçüm arasındaki farkı alarak gerçek I/O hızını hesapla
def disk_io_rate(interval=1):
    before = psutil.disk_io_counters(perdisk=True)
    time.sleep(interval)
    after = psutil.disk_io_counters(perdisk=True)
    
    for disk_name in after:
        if disk_name not in before:
            continue
        
        read_bytes = (after[disk_name].read_bytes - before[disk_name].read_bytes) / interval
        write_bytes = (after[disk_name].write_bytes - before[disk_name].write_bytes) / interval
        
        print(f"{disk_name}:")
        print(f"  Okuma  : {read_bytes / 1024:.1f} KB/s")
        print(f"  Yazma  : {write_bytes / 1024:.1f} KB/s")

disk_io_rate()

Network Bağlantılarını ve Trafiği İzlemek

Network tarafında hem trafik istatistikleri hem de aktif bağlantıları izlemek mümkün. Bir sunucuda beklenmedik outbound bağlantı mı var? Belirli bir porta aşırı bağlantı yağmuru mu geliyor? psutil bunları sorgulamak için pratik bir yol sunar.

import psutil
import time

def network_stats(interval=2):
    before = psutil.net_io_counters(pernic=True)
    time.sleep(interval)
    after = psutil.net_io_counters(pernic=True)
    
    print("=== Network Arayüz İstatistikleri ===n")
    
    for iface in after:
        if iface not in before:
            continue
        
        # Sadece aktif arayüzleri göster
        sent_rate = (after[iface].bytes_sent - before[iface].bytes_sent) / interval
        recv_rate = (after[iface].bytes_recv - before[iface].bytes_recv) / interval
        
        if sent_rate == 0 and recv_rate == 0:
            continue
        
        print(f"Arayüz: {iface}")
        print(f"  Gönderme Hızı : {sent_rate / 1024:.2f} KB/s")
        print(f"  Alma Hızı   : {recv_rate / 1024:.2f} KB/s")
        print(f"  Toplam Gönderilen: {after[iface].bytes_sent / (1024**2):.1f} MB")
        print(f"  Toplam Alınan  : {after[iface].bytes_recv / (1024**2):.1f} MB")
        print(f"  Paket Hatası  : {after[iface].errin + after[iface].errout}")
        print()

network_stats()

Process Yönetimi ile Gerçek Dünya Senaryosu

Şimdi işin pratik kısmına gelelim. Diyelim ki bir production sunucusunda zaman zaman belirli bir process RAM kullanımını patlattıktan sonra sistemi yavaşlatıyor ve siz kill etmek zorunda kalıyorsunuz. Bu durumu otomatize etmek mümkün.

import psutil
import logging
import time

logging.basicConfig(
    filename='/var/log/process_monitor.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def find_memory_hogs(threshold_percent=10.0):
    """RAM kullanımı eşik değerini aşan processleri bul."""
    hogs = []
    
    for proc in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', 'cpu_percent', 'status']):
        try:
            info = proc.info
            if info['memory_percent'] and info['memory_percent'] > threshold_percent:
                hogs.append(info)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            # Process ölmüş olabilir ya da root yetkisi gerekebilir
            continue
    
    # RAM kullanımına göre sırala
    hogs.sort(key=lambda x: x['memory_percent'], reverse=True)
    return hogs

def monitor_processes(check_interval=30, mem_threshold=10.0):
    print(f"Process izleme başladı. Eşik: %{mem_threshold} RAM")
    print("Çıkmak için Ctrl+Cn")
    
    while True:
        hogs = find_memory_hogs(threshold_percent=mem_threshold)
        
        if hogs:
            print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Yüksek RAM kullanan processler:")
            for proc in hogs:
                msg = (f"PID: {proc['pid']} | Ad: {proc['name']} | "
                       f"Kullanıcı: {proc['username']} | "
                       f"RAM: {proc['memory_percent']:.2f}%")
                print(f"  {msg}")
                logging.warning(msg)
        else:
            print(f"[{time.strftime('%H:%M:%S')}] Sistem normal, yüksek RAM kullanan process yok.")
        
        time.sleep(check_interval)

if __name__ == '__main__':
    monitor_processes()

Kapsamlı Sistem Raporu Scripti

Tüm bunları bir araya getiren, cronjob ile düzenli olarak çalıştırılabilecek ve sonuçları bir dosyaya yazan kapsamlı bir script örneği:

#!/usr/bin/env python3
"""
Sistem Kaynak Raporu
Kullanım: python3 sys_report.py [--output /path/to/report.log]
"""

import psutil
import json
import datetime
import argparse
import sys

def collect_metrics():
    """Tüm sistem metriklerini topla ve dict olarak döndür."""
    
    metrics = {
        'timestamp': datetime.datetime.now().isoformat(),
        'hostname': '',
        'cpu': {},
        'memory': {},
        'disk': [],
        'network': {},
        'top_processes': []
    }
    
    # Hostname
    try:
        import socket
        metrics['hostname'] = socket.gethostname()
    except Exception:
        metrics['hostname'] = 'unknown'
    
    # CPU
    metrics['cpu'] = {
        'percent': psutil.cpu_percent(interval=1),
        'count_physical': psutil.cpu_count(logical=False),
        'count_logical': psutil.cpu_count(logical=True),
    }
    
    try:
        load = psutil.getloadavg()
        metrics['cpu']['load_avg'] = {
            '1min': round(load[0], 2),
            '5min': round(load[1], 2),
            '15min': round(load[2], 2)
        }
    except AttributeError:
        pass
    
    # Memory
    vm = psutil.virtual_memory()
    metrics['memory'] = {
        'total_gb': round(vm.total / (1024**3), 2),
        'used_gb': round(vm.used / (1024**3), 2),
        'available_gb': round(vm.available / (1024**3), 2),
        'percent': vm.percent
    }
    
    # Disk
    for partition in psutil.disk_partitions():
        try:
            usage = psutil.disk_usage(partition.mountpoint)
            metrics['disk'].append({
                'device': partition.device,
                'mountpoint': partition.mountpoint,
                'fstype': partition.fstype,
                'total_gb': round(usage.total / (1024**3), 2),
                'used_gb': round(usage.used / (1024**3), 2),
                'free_gb': round(usage.free / (1024**3), 2),
                'percent': usage.percent,
                'critical': usage.percent >= 90,
                'warning': 75 <= usage.percent < 90
            })
        except (PermissionError, OSError):
            continue
    
    # Top 5 process (RAM'e göre)
    procs = []
    for proc in psutil.process_iter(['pid', 'name', 'memory_percent', 'cpu_percent']):
        try:
            procs.append(proc.info)
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
    
    procs.sort(key=lambda x: x.get('memory_percent') or 0, reverse=True)
    metrics['top_processes'] = procs[:5]
    
    return metrics

def generate_text_report(metrics):
    """Metrics dict'inden okunabilir bir rapor oluştur."""
    lines = []
    lines.append("=" * 50)
    lines.append(f"SİSTEM RAPORU - {metrics['hostname']}")
    lines.append(f"Zaman: {metrics['timestamp']}")
    lines.append("=" * 50)
    
    cpu = metrics['cpu']
    lines.append(f"n[CPU] Kullanim: %{cpu['percent']}")
    if 'load_avg' in cpu:
        la = cpu['load_avg']
        lines.append(f"[CPU] Load Average: {la['1min']} / {la['5min']} / {la['15min']}")
    
    mem = metrics['memory']
    lines.append(f"n[RAM] Kullanan: {mem['used_gb']} GB / {mem['total_gb']} GB (%{mem['percent']})")
    
    lines.append("n[DISK]")
    for d in metrics['disk']:
        status = "KRİTİK" if d['critical'] else ("UYARI" if d['warning'] else "OK")
        lines.append(f"  {d['mountpoint']}: %{d['percent']} dolu [{status}]")
    
    lines.append("n[TOP 5 PROCESS - RAM]")
    for p in metrics['top_processes']:
        mem_pct = p.get('memory_percent') or 0
        lines.append(f"  PID {p['pid']:6d} | {p['name'][:20]:20s} | RAM: %{mem_pct:.2f}")
    
    lines.append("")
    return "n".join(lines)

def main():
    parser = argparse.ArgumentParser(description='Sistem Kaynak Raporu')
    parser.add_argument('--output', help='Rapor dosyası yolu', default=None)
    parser.add_argument('--json', action='store_true', help='JSON formatında çıktı ver')
    args = parser.parse_args()
    
    metrics = collect_metrics()
    
    if args.json:
        output = json.dumps(metrics, indent=2, ensure_ascii=False)
    else:
        output = generate_text_report(metrics)
    
    if args.output:
        with open(args.output, 'a', encoding='utf-8') as f:
            f.write(output + "n")
        print(f"Rapor yazıldı: {args.output}")
    else:
        print(output)
    
    # Kritik disk doluluk varsa exit code 1 döndür (cronjob alerting için)
    critical_disks = [d for d in metrics['disk'] if d['critical']]
    if critical_disks or metrics['memory']['percent'] > 90:
        sys.exit(1)
    
    sys.exit(0)

if __name__ == '__main__':
    main()

Bu scripti cronjob ile çalıştırmak için:

# Her 15 dakikada bir çalıştır, kritik durumlarda mail gönder
*/15 * * * * /usr/bin/python3 /opt/scripts/sys_report.py --output /var/log/sys_report.log || mail -s "UYARI: Sistem Kaynak Alarmı" [email protected] < /var/log/sys_report.log

Sistem Uptime ve Boot Bilgisi

Monitoring senaryolarında uptime bilgisi de önemli. Beklenmedik bir reboot yaşandıysa bunu log’dan görmek yerine direkt sorgulamak isteyebilirsiniz:

import psutil
import datetime

boot_time = psutil.boot_time()
boot_dt = datetime.datetime.fromtimestamp(boot_time)
uptime_seconds = (datetime.datetime.now() - boot_dt).total_seconds()

days = int(uptime_seconds // 86400)
hours = int((uptime_seconds % 86400) // 3600)
minutes = int((uptime_seconds % 3600) // 60)

print(f"Son Başlangıç : {boot_dt.strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Uptime     : {days} gün, {hours} saat, {minutes} dakika")

Dikkat Edilmesi Gereken Noktalar

psutil kullanırken birkaç pratik noktaya dikkat etmek gerekir:

  • AccessDenied istisnası: Root yetkisi olmadan bazı processlerin bilgilerine erişilemez. Her zaman try/except bloğu kullanın.
  • NoSuchProcess istisnası: Process listesini çekerken bir process sonlanmış olabilir. Bu tamamen normaldir, yakalamanız yeterli.
  • cpu_percent() ilk çağrı: İlk çağrıda 0.0 dönebilir. interval parametresi verip bloklamak ya da ardışık iki çağrı yapmak gerekir.
  • Windows’ta bazı özellikler eksik: getloadavg(), sensors_temperatures() gibi fonksiyonlar Windows’ta ya yoktur ya da çok sınırlıdır. Platform kontrolü yapın.
  • Production’da interval seçimi: Çok kısa intervallerle çok sık polling yapmak, izlediğiniz sistemin kendisine yük bindirmeye başlar. 30 saniye ile 1 dakikalık aralıklar genellikle makuldür.

Sonuç

psutil, Python ile sistem otomasyonu yapan her sysadmin’in araç kutusunda olması gereken kütüphanelerin başında geliyor. Karmaşık shell scriptleri yazmaktan, platforma özgü komutların çıktısını parse etmekten kurtarıyor ve topladığınız veriyi doğrudan kullanılabilir Python nesneleri olarak sunuyor.

Burada anlattıklarımı başlangıç noktası olarak düşünün. Gerçek dünyada bu scriptleri Grafana/InfluxDB stack’iyle birleştirebilir, Prometheus exporter yazabilir ya da mevcut alerting sisteminize entegre edebilirsiniz. Ancak temel mantık hep aynı: veriyi topla, eşikleri kontrol et, zamanında uyar.

En pratik tavsiyem şu: Önce kendi ortamınızın “normal” değerlerini belirleyin. CPU’nuz genellikle %30’da mı çalışıyor, %70’te mi? RAM’inizin yüzde kaçı sürekli dolu? Bu baseline’ı bilmeden alert eşikleri koymak, ya hiç çalmayan ya da sürekli çalan alarm sistemleriyle sonuçlanır. psutil size veriyi verir, ama o veriyi yorumlamak hala sizin işiniz.

Yorum yapın