@@ -343,6 +343,11 @@ type systemInfoCache struct {
343343 info * collector.SystemInfo
344344 timestamp time.Time
345345 ttl time.Duration
346+
347+ // Network rate tracking
348+ prevNetBytesSent uint64
349+ prevNetBytesRecv uint64
350+ prevNetTimestamp time.Time
346351}
347352
348353var infoCache = & systemInfoCache {
@@ -497,6 +502,25 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
497502 info .SwapOut = swapInfo .Sout
498503 }
499504
505+ // Page faults from /proc/vmstat (Linux only)
506+ if runtime .GOOS == "linux" {
507+ if data , err := os .ReadFile ("/proc/vmstat" ); err == nil {
508+ var totalFaults uint64
509+ for _ , line := range strings .Split (string (data ), "\n " ) {
510+ switch {
511+ case strings .HasPrefix (line , "pgmajfault " ):
512+ info .PageFaultsMajor = parseUint64 (strings .TrimPrefix (line , "pgmajfault " ))
513+ case strings .HasPrefix (line , "pgfault " ):
514+ totalFaults = parseUint64 (strings .TrimPrefix (line , "pgfault " ))
515+ }
516+ }
517+ // Minor faults = total faults - major faults
518+ if totalFaults > info .PageFaultsMajor {
519+ info .PageFaultsMinor = totalFaults - info .PageFaultsMajor
520+ }
521+ }
522+ }
523+
500524 // ==========================================================================
501525 // Disk Information
502526 // ==========================================================================
@@ -553,6 +577,14 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
553577 if totalWriteOps > 0 && totalWriteTime > 0 {
554578 info .DiskLatencyWrite = float64 (totalWriteTime ) / float64 (totalWriteOps )
555579 }
580+
581+ // Calculate IOPS (operations per second based on IO time)
582+ // DiskIOTime is in milliseconds, represents time spent doing I/O
583+ totalOps := totalReadOps + totalWriteOps
584+ if totalOps > 0 && totalIOTime > 0 {
585+ // IOPS = total operations / (IO time in seconds)
586+ info .DiskIOPS = float64 (totalOps ) / (float64 (totalIOTime ) / 1000.0 )
587+ }
556588 }
557589
558590 // Per-partition disk info
@@ -595,6 +627,24 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
595627 info .NetworkDropsOut = total .Dropout
596628 info .NetworkFifoIn = total .Fifoin
597629 info .NetworkFifoOut = total .Fifoout
630+
631+ // Calculate network rates using cached previous values
632+ now := time .Now ()
633+ infoCache .mu .Lock ()
634+ if ! infoCache .prevNetTimestamp .IsZero () {
635+ elapsed := now .Sub (infoCache .prevNetTimestamp ).Seconds ()
636+ if elapsed > 0 && total .BytesSent >= infoCache .prevNetBytesSent {
637+ info .NetworkBytesSentRate = float64 (total .BytesSent - infoCache .prevNetBytesSent ) / elapsed
638+ }
639+ if elapsed > 0 && total .BytesRecv >= infoCache .prevNetBytesRecv {
640+ info .NetworkBytesRecvRate = float64 (total .BytesRecv - infoCache .prevNetBytesRecv ) / elapsed
641+ }
642+ }
643+ // Update previous values for next calculation
644+ infoCache .prevNetBytesSent = total .BytesSent
645+ infoCache .prevNetBytesRecv = total .BytesRecv
646+ infoCache .prevNetTimestamp = now
647+ infoCache .mu .Unlock ()
598648 }
599649
600650 // TCP connection states
@@ -626,6 +676,33 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
626676 }
627677 }
628678
679+ // TCP Retransmits from /proc/net/snmp (Linux only)
680+ if runtime .GOOS == "linux" {
681+ if data , err := os .ReadFile ("/proc/net/snmp" ); err == nil {
682+ lines := strings .Split (string (data ), "\n " )
683+ for i , line := range lines {
684+ // Find the Tcp: header line
685+ if strings .HasPrefix (line , "Tcp:" ) && ! strings .Contains (line , " " ) == false {
686+ // Check if this is the header line (contains column names)
687+ if strings .Contains (line , "RtoAlgorithm" ) {
688+ // Next line contains values
689+ if i + 1 < len (lines ) {
690+ values := strings .Fields (lines [i + 1 ])
691+ // RetransSegs is at index 12 (0-indexed)
692+ // Columns: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens
693+ // AttemptFails EstabResets CurrEstab InSegs OutSegs RetransSegs
694+ // InErrs OutRsts InCsumErrors
695+ if len (values ) > 12 {
696+ info .TCPRetransmits = parseUint64 (values [12 ])
697+ }
698+ }
699+ break
700+ }
701+ }
702+ }
703+ }
704+ }
705+
629706 // Per-interface network info
630707 netInterfaces , err := net .Interfaces ()
631708 if err == nil {
@@ -751,6 +828,17 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
751828 }
752829 }
753830 }
831+
832+ // System calls - aggregate from all processes' /proc/[pid]/io
833+ // This counts read (syscr) and write (syscw) system calls
834+ if procs != nil {
835+ for _ , p := range procs {
836+ ioCounters , err := p .IOCounters ()
837+ if err == nil {
838+ info .SystemCalls += ioCounters .ReadCount + ioCounters .WriteCount
839+ }
840+ }
841+ }
754842 }
755843
756844 // ==========================================================================
@@ -760,6 +848,8 @@ func (c *HostCollector) GetSystemInfo() (*collector.SystemInfo, error) {
760848 if info .IsContainer {
761849 info .ContainerID = getContainerID ()
762850 info .ContainerRuntime = detectContainerRuntime ()
851+ info .ContainerName = getContainerName ()
852+ info .ContainerImage = getContainerImage ()
763853 }
764854
765855 info .IsVirtualized , info .VirtualizationType = detectVirtualization ()
@@ -1007,3 +1097,44 @@ func getHostnameFallback() string {
10071097 }
10081098 return "unknown"
10091099}
1100+
1101+ // getContainerName returns the container name from environment variables
1102+ func getContainerName () string {
1103+ // Check common environment variables for container name
1104+ // Kubernetes sets HOSTNAME to pod name
1105+ if name := os .Getenv ("CONTAINER_NAME" ); name != "" {
1106+ return name
1107+ }
1108+ // Docker Compose sets COMPOSE_PROJECT_NAME and service name
1109+ if project := os .Getenv ("COMPOSE_PROJECT_NAME" ); project != "" {
1110+ if service := os .Getenv ("COMPOSE_SERVICE" ); service != "" {
1111+ return project + "_" + service
1112+ }
1113+ }
1114+ // Kubernetes pod name
1115+ if podName := os .Getenv ("POD_NAME" ); podName != "" {
1116+ return podName
1117+ }
1118+ // Try to get from Docker environment
1119+ if name := os .Getenv ("DOCKER_CONTAINER_NAME" ); name != "" {
1120+ return name
1121+ }
1122+ return ""
1123+ }
1124+
1125+ // getContainerImage returns the container image from environment variables
1126+ func getContainerImage () string {
1127+ // Check common environment variables for container image
1128+ if image := os .Getenv ("CONTAINER_IMAGE" ); image != "" {
1129+ return image
1130+ }
1131+ // Kubernetes commonly uses this downward API field
1132+ if image := os .Getenv ("POD_IMAGE" ); image != "" {
1133+ return image
1134+ }
1135+ // Docker environment variable
1136+ if image := os .Getenv ("DOCKER_IMAGE" ); image != "" {
1137+ return image
1138+ }
1139+ return ""
1140+ }
0 commit comments