1// Copyright 2015 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package prometheus
15
16import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs"
17
18type processCollector struct {
19	pid             int
20	collectFn       func(chan<- Metric)
21	pidFn           func() (int, error)
22	cpuTotal        Counter
23	openFDs, maxFDs Gauge
24	vsize, rss      Gauge
25	startTime       Gauge
26}
27
28// NewProcessCollector returns a collector which exports the current state of
29// process metrics including cpu, memory and file descriptor usage as well as
30// the process start time for the given process id under the given namespace.
31func NewProcessCollector(pid int, namespace string) *processCollector {
32	return NewProcessCollectorPIDFn(
33		func() (int, error) { return pid, nil },
34		namespace,
35	)
36}
37
38// NewProcessCollectorPIDFn returns a collector which exports the current state
39// of process metrics including cpu, memory and file descriptor usage as well
40// as the process start time under the given namespace. The given pidFn is
41// called on each collect and is used to determine the process to export
42// metrics for.
43func NewProcessCollectorPIDFn(
44	pidFn func() (int, error),
45	namespace string,
46) *processCollector {
47	c := processCollector{
48		pidFn:     pidFn,
49		collectFn: func(chan<- Metric) {},
50
51		cpuTotal: NewCounter(CounterOpts{
52			Namespace: namespace,
53			Name:      "process_cpu_seconds_total",
54			Help:      "Total user and system CPU time spent in seconds.",
55		}),
56		openFDs: NewGauge(GaugeOpts{
57			Namespace: namespace,
58			Name:      "process_open_fds",
59			Help:      "Number of open file descriptors.",
60		}),
61		maxFDs: NewGauge(GaugeOpts{
62			Namespace: namespace,
63			Name:      "process_max_fds",
64			Help:      "Maximum number of open file descriptors.",
65		}),
66		vsize: NewGauge(GaugeOpts{
67			Namespace: namespace,
68			Name:      "process_virtual_memory_bytes",
69			Help:      "Virtual memory size in bytes.",
70		}),
71		rss: NewGauge(GaugeOpts{
72			Namespace: namespace,
73			Name:      "process_resident_memory_bytes",
74			Help:      "Resident memory size in bytes.",
75		}),
76		startTime: NewGauge(GaugeOpts{
77			Namespace: namespace,
78			Name:      "process_start_time_seconds",
79			Help:      "Start time of the process since unix epoch in seconds.",
80		}),
81	}
82
83	// Set up process metric collection if supported by the runtime.
84	if _, err := procfs.NewStat(); err == nil {
85		c.collectFn = c.processCollect
86	}
87
88	return &c
89}
90
91// Describe returns all descriptions of the collector.
92func (c *processCollector) Describe(ch chan<- *Desc) {
93	ch <- c.cpuTotal.Desc()
94	ch <- c.openFDs.Desc()
95	ch <- c.maxFDs.Desc()
96	ch <- c.vsize.Desc()
97	ch <- c.rss.Desc()
98	ch <- c.startTime.Desc()
99}
100
101// Collect returns the current state of all metrics of the collector.
102func (c *processCollector) Collect(ch chan<- Metric) {
103	c.collectFn(ch)
104}
105
106// TODO(ts): Bring back error reporting by reverting 7faf9e7 as soon as the
107// client allows users to configure the error behavior.
108func (c *processCollector) processCollect(ch chan<- Metric) {
109	pid, err := c.pidFn()
110	if err != nil {
111		return
112	}
113
114	p, err := procfs.NewProc(pid)
115	if err != nil {
116		return
117	}
118
119	if stat, err := p.NewStat(); err == nil {
120		c.cpuTotal.Set(stat.CPUTime())
121		ch <- c.cpuTotal
122		c.vsize.Set(float64(stat.VirtualMemory()))
123		ch <- c.vsize
124		c.rss.Set(float64(stat.ResidentMemory()))
125		ch <- c.rss
126
127		if startTime, err := stat.StartTime(); err == nil {
128			c.startTime.Set(startTime)
129			ch <- c.startTime
130		}
131	}
132
133	if fds, err := p.FileDescriptorsLen(); err == nil {
134		c.openFDs.Set(float64(fds))
135		ch <- c.openFDs
136	}
137
138	if limits, err := p.NewLimits(); err == nil {
139		c.maxFDs.Set(float64(limits.OpenFiles))
140		ch <- c.maxFDs
141	}
142}
143