Skip to content

Commit 38950f4

Browse files
committed
feat: add process snapshot support and update metrics parsing logic
1 parent 7e73df4 commit 38950f4

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

internal/prometheus/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
metrics.txt
1+
metrics.txt
2+
process_snapshot.txt

internal/prometheus/process_exporter_parser.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ func parseProcessLine(line string, processMetrics map[string]*processData) error
124124
pm.numProcs = int(value)
125125

126126
case "namedprocess_namegroup_cpu_seconds_total":
127-
pm.cpuSecondsTotal = value
127+
// Sum across all modes (user + system)
128+
// process_exporter provides: mode="user" and mode="system"
129+
pm.cpuSecondsTotal += value
128130

129131
case "namedprocess_namegroup_memory_bytes":
130132
// Only use resident memory (RSS)
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package prometheus
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestParseProcessExporterMetrics(t *testing.T) {
8+
// Sample process_exporter output with mode labels (user/system)
9+
input := `# HELP namedprocess_namegroup_num_procs Number of processes in this group
10+
# TYPE namedprocess_namegroup_num_procs gauge
11+
namedprocess_namegroup_num_procs{groupname="nginx"} 4
12+
namedprocess_namegroup_num_procs{groupname="postgres"} 1
13+
14+
# HELP namedprocess_namegroup_cpu_seconds_total CPU time consumed by this process group
15+
# TYPE namedprocess_namegroup_cpu_seconds_total counter
16+
namedprocess_namegroup_cpu_seconds_total{groupname="nginx",mode="system"} 123.45
17+
namedprocess_namegroup_cpu_seconds_total{groupname="nginx",mode="user"} 456.78
18+
namedprocess_namegroup_cpu_seconds_total{groupname="postgres",mode="system"} 10.5
19+
namedprocess_namegroup_cpu_seconds_total{groupname="postgres",mode="user"} 20.3
20+
21+
# HELP namedprocess_namegroup_memory_bytes Memory used by this process group
22+
# TYPE namedprocess_namegroup_memory_bytes gauge
23+
namedprocess_namegroup_memory_bytes{groupname="nginx",memtype="resident"} 104857600
24+
namedprocess_namegroup_memory_bytes{groupname="nginx",memtype="virtual"} 209715200
25+
namedprocess_namegroup_memory_bytes{groupname="postgres",memtype="resident"} 52428800
26+
namedprocess_namegroup_memory_bytes{groupname="postgres",memtype="virtual"} 104857600
27+
`
28+
29+
snapshots, err := ParseProcessExporterMetrics([]byte(input))
30+
if err != nil {
31+
t.Fatalf("ParseProcessExporterMetrics failed: %v", err)
32+
}
33+
34+
// Should have 2 process groups
35+
if len(snapshots) != 2 {
36+
t.Fatalf("Expected 2 snapshots, got %d", len(snapshots))
37+
}
38+
39+
// Find nginx snapshot
40+
var nginx *ProcessExporterMetricSnapshot
41+
for i := range snapshots {
42+
if snapshots[i].Name == "nginx" {
43+
nginx = &snapshots[i]
44+
break
45+
}
46+
}
47+
48+
if nginx == nil {
49+
t.Fatal("nginx snapshot not found")
50+
}
51+
52+
// Verify nginx metrics
53+
if nginx.NumProcs != 4 {
54+
t.Errorf("Expected nginx.NumProcs=4, got %d", nginx.NumProcs)
55+
}
56+
57+
// CPU should be sum of user + system = 123.45 + 456.78 = 580.23
58+
expectedCPU := 123.45 + 456.78
59+
if nginx.CPUSecondsTotal != expectedCPU {
60+
t.Errorf("Expected nginx.CPUSecondsTotal=%.2f, got %.2f", expectedCPU, nginx.CPUSecondsTotal)
61+
}
62+
63+
// Memory should be resident (RSS)
64+
if nginx.MemoryBytes != 104857600 {
65+
t.Errorf("Expected nginx.MemoryBytes=104857600, got %d", nginx.MemoryBytes)
66+
}
67+
68+
// Find postgres snapshot
69+
var postgres *ProcessExporterMetricSnapshot
70+
for i := range snapshots {
71+
if snapshots[i].Name == "postgres" {
72+
postgres = &snapshots[i]
73+
break
74+
}
75+
}
76+
77+
if postgres == nil {
78+
t.Fatal("postgres snapshot not found")
79+
}
80+
81+
// Verify postgres metrics
82+
if postgres.NumProcs != 1 {
83+
t.Errorf("Expected postgres.NumProcs=1, got %d", postgres.NumProcs)
84+
}
85+
86+
// CPU should be sum of user + system = 10.5 + 20.3 = 30.8
87+
expectedPostgresCPU := 10.5 + 20.3
88+
if postgres.CPUSecondsTotal != expectedPostgresCPU {
89+
t.Errorf("Expected postgres.CPUSecondsTotal=%.2f, got %.2f", expectedPostgresCPU, postgres.CPUSecondsTotal)
90+
}
91+
92+
if postgres.MemoryBytes != 52428800 {
93+
t.Errorf("Expected postgres.MemoryBytes=52428800, got %d", postgres.MemoryBytes)
94+
}
95+
}
96+
97+
func TestParseProcessExporterMetrics_OnlyNumProcs(t *testing.T) {
98+
// Test with process that only has num_procs > 0
99+
input := `namedprocess_namegroup_num_procs{groupname="test"} 2
100+
namedprocess_namegroup_cpu_seconds_total{groupname="test",mode="system"} 0
101+
namedprocess_namegroup_cpu_seconds_total{groupname="test",mode="user"} 0
102+
namedprocess_namegroup_memory_bytes{groupname="test",memtype="resident"} 1024
103+
`
104+
105+
snapshots, err := ParseProcessExporterMetrics([]byte(input))
106+
if err != nil {
107+
t.Fatalf("ParseProcessExporterMetrics failed: %v", err)
108+
}
109+
110+
if len(snapshots) != 1 {
111+
t.Fatalf("Expected 1 snapshot, got %d", len(snapshots))
112+
}
113+
114+
if snapshots[0].Name != "test" {
115+
t.Errorf("Expected name='test', got '%s'", snapshots[0].Name)
116+
}
117+
118+
if snapshots[0].NumProcs != 2 {
119+
t.Errorf("Expected NumProcs=2, got %d", snapshots[0].NumProcs)
120+
}
121+
}
122+
123+
func TestParseProcessExporterMetrics_ZeroProcs(t *testing.T) {
124+
// Process with 0 procs should be filtered out
125+
input := `namedprocess_namegroup_num_procs{groupname="dead"} 0
126+
namedprocess_namegroup_cpu_seconds_total{groupname="dead",mode="system"} 100
127+
namedprocess_namegroup_cpu_seconds_total{groupname="dead",mode="user"} 200
128+
`
129+
130+
snapshots, err := ParseProcessExporterMetrics([]byte(input))
131+
if err != nil {
132+
t.Fatalf("ParseProcessExporterMetrics failed: %v", err)
133+
}
134+
135+
// Should be filtered out because numProcs == 0
136+
if len(snapshots) != 0 {
137+
t.Fatalf("Expected 0 snapshots (filtered), got %d", len(snapshots))
138+
}
139+
}

0 commit comments

Comments
 (0)