blob: 55176d58ce6a213fe4008f6b2f8630709bd031cc [file] [log] [blame]
khenaidooffe076b2019-01-15 16:08:08 -05001// 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 (
17 "errors"
18 "os"
19
20 "github.com/prometheus/procfs"
21)
22
23type processCollector struct {
24 collectFn func(chan<- Metric)
25 pidFn func() (int, error)
26 reportErrors bool
27 cpuTotal *Desc
28 openFDs, maxFDs *Desc
29 vsize, maxVsize *Desc
30 rss *Desc
31 startTime *Desc
32}
33
34// ProcessCollectorOpts defines the behavior of a process metrics collector
35// created with NewProcessCollector.
36type ProcessCollectorOpts struct {
37 // PidFn returns the PID of the process the collector collects metrics
38 // for. It is called upon each collection. By default, the PID of the
39 // current process is used, as determined on construction time by
40 // calling os.Getpid().
41 PidFn func() (int, error)
42 // If non-empty, each of the collected metrics is prefixed by the
43 // provided string and an underscore ("_").
44 Namespace string
45 // If true, any error encountered during collection is reported as an
46 // invalid metric (see NewInvalidMetric). Otherwise, errors are ignored
47 // and the collected metrics will be incomplete. (Possibly, no metrics
48 // will be collected at all.) While that's usually not desired, it is
49 // appropriate for the common "mix-in" of process metrics, where process
50 // metrics are nice to have, but failing to collect them should not
51 // disrupt the collection of the remaining metrics.
52 ReportErrors bool
53}
54
55// NewProcessCollector returns a collector which exports the current state of
56// process metrics including CPU, memory and file descriptor usage as well as
57// the process start time. The detailed behavior is defined by the provided
58// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a
59// collector for the current process with an empty namespace string and no error
60// reporting.
61//
62// Currently, the collector depends on a Linux-style proc filesystem and
63// therefore only exports metrics for Linux.
64//
65// Note: An older version of this function had the following signature:
66//
67// NewProcessCollector(pid int, namespace string) Collector
68//
69// Most commonly, it was called as
70//
71// NewProcessCollector(os.Getpid(), "")
72//
73// The following call of the current version is equivalent to the above:
74//
75// NewProcessCollector(ProcessCollectorOpts{})
76func NewProcessCollector(opts ProcessCollectorOpts) Collector {
77 ns := ""
78 if len(opts.Namespace) > 0 {
79 ns = opts.Namespace + "_"
80 }
81
82 c := &processCollector{
83 reportErrors: opts.ReportErrors,
84 cpuTotal: NewDesc(
85 ns+"process_cpu_seconds_total",
86 "Total user and system CPU time spent in seconds.",
87 nil, nil,
88 ),
89 openFDs: NewDesc(
90 ns+"process_open_fds",
91 "Number of open file descriptors.",
92 nil, nil,
93 ),
94 maxFDs: NewDesc(
95 ns+"process_max_fds",
96 "Maximum number of open file descriptors.",
97 nil, nil,
98 ),
99 vsize: NewDesc(
100 ns+"process_virtual_memory_bytes",
101 "Virtual memory size in bytes.",
102 nil, nil,
103 ),
104 maxVsize: NewDesc(
105 ns+"process_virtual_memory_max_bytes",
106 "Maximum amount of virtual memory available in bytes.",
107 nil, nil,
108 ),
109 rss: NewDesc(
110 ns+"process_resident_memory_bytes",
111 "Resident memory size in bytes.",
112 nil, nil,
113 ),
114 startTime: NewDesc(
115 ns+"process_start_time_seconds",
116 "Start time of the process since unix epoch in seconds.",
117 nil, nil,
118 ),
119 }
120
121 if opts.PidFn == nil {
122 pid := os.Getpid()
123 c.pidFn = func() (int, error) { return pid, nil }
124 } else {
125 c.pidFn = opts.PidFn
126 }
127
128 // Set up process metric collection if supported by the runtime.
129 if _, err := procfs.NewStat(); err == nil {
130 c.collectFn = c.processCollect
131 } else {
132 c.collectFn = func(ch chan<- Metric) {
133 c.reportError(ch, nil, errors.New("process metrics not supported on this platform"))
134 }
135 }
136
137 return c
138}
139
140// Describe returns all descriptions of the collector.
141func (c *processCollector) Describe(ch chan<- *Desc) {
142 ch <- c.cpuTotal
143 ch <- c.openFDs
144 ch <- c.maxFDs
145 ch <- c.vsize
146 ch <- c.maxVsize
147 ch <- c.rss
148 ch <- c.startTime
149}
150
151// Collect returns the current state of all metrics of the collector.
152func (c *processCollector) Collect(ch chan<- Metric) {
153 c.collectFn(ch)
154}
155
156func (c *processCollector) processCollect(ch chan<- Metric) {
157 pid, err := c.pidFn()
158 if err != nil {
159 c.reportError(ch, nil, err)
160 return
161 }
162
163 p, err := procfs.NewProc(pid)
164 if err != nil {
165 c.reportError(ch, nil, err)
166 return
167 }
168
169 if stat, err := p.NewStat(); err == nil {
170 ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime())
171 ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory()))
172 ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
173 if startTime, err := stat.StartTime(); err == nil {
174 ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
175 } else {
176 c.reportError(ch, c.startTime, err)
177 }
178 } else {
179 c.reportError(ch, nil, err)
180 }
181
182 if fds, err := p.FileDescriptorsLen(); err == nil {
183 ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
184 } else {
185 c.reportError(ch, c.openFDs, err)
186 }
187
188 if limits, err := p.NewLimits(); err == nil {
189 ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
190 ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace))
191 } else {
192 c.reportError(ch, nil, err)
193 }
194}
195
196func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
197 if !c.reportErrors {
198 return
199 }
200 if desc == nil {
201 desc = NewInvalidDesc(err)
202 }
203 ch <- NewInvalidMetric(desc, err)
204}