Skip to content

Commit

Permalink
Report number of open file handles on Windows (#8329)
Browse files Browse the repository at this point in the history
Now file handles/descriptors are reported by the same name on all platforms which support either one of them. New "unified" name is beat.handles.open. I other FD related metrics to beat.handles.limit.hard and beat.handles.limit.soft.

I also moved registration of callbacks after initialization of variables used inside the callbacks. Failures of metrics which caused by environment problems (e.g. Beat cannot query something) are reported once for the first time. Then it's not attempted anymore.
Errors which are due to programming errors, for example initializing beatProcessStats, does not let the Beat start. It helps us noticing errors before others and can be easily corrected.
  • Loading branch information
kvch authored Sep 19, 2018
1 parent 8137c44 commit 0d28221
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 95 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ https://github.com/elastic/beats/compare/v6.4.0...master[Check the HEAD diff]
- Count HTTP 429 responses in the elasticsearch output {pull}8056[8056]
- Report configured queue type. {pull}8091[8091]
- Added the `add_process_metadata` processor to enrich events with process information. {pull}6789[6789]
- Report number of open file handles on Windows. {pull}8329[8329]

*Auditbeat*

Expand Down
4 changes: 2 additions & 2 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ Apache License 2.0

--------------------------------------------------------------------
Dependency: github.com/elastic/go-sysinfo
Revision: b30cfd89b3c329a5b84f237573c8422730d2e170
Revision: 7b021494a9562d0c3f0422d49b9980709c5650e9
License type (autodetected): Apache-2.0
./vendor/github.com/elastic/go-sysinfo/LICENSE.txt:
--------------------------------------------------------------------
Expand Down Expand Up @@ -486,7 +486,7 @@ Apache License 2.0

--------------------------------------------------------------------
Dependency: github.com/elastic/go-windows
Revision: a4ab469ab3d6f167cba8cb515c3273c6b56f9fc3
Revision: bb1581babc04d5cb29a2bfa7a9ac6781c730c8dd
License type (autodetected): Apache-2.0
./vendor/github.com/elastic/go-windows/LICENSE.txt:
--------------------------------------------------------------------
Expand Down
78 changes: 13 additions & 65 deletions libbeat/cmd/instance/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,33 +40,37 @@ func init() {
}

func setupMetrics(name string) error {
monitoring.NewFunc(beatMetrics, "memstats", reportMemStats, monitoring.Report)
monitoring.NewFunc(beatMetrics, "cpu", reportBeatCPU, monitoring.Report)

monitoring.NewFunc(systemMetrics, "cpu", reportSystemCPUUsage, monitoring.Report)

setupPlatformSpecificMetrics()

beatProcessStats = &process.Stats{
Procs: []string{name},
EnvWhitelist: nil,
CpuTicks: true,
CacheCmdLine: true,
IncludeTop: process.IncludeTopConfig{},
}

err := beatProcessStats.Init()
if err != nil {
return err
}

monitoring.NewFunc(beatMetrics, "memstats", reportMemStats, monitoring.Report)
monitoring.NewFunc(beatMetrics, "cpu", reportBeatCPU, monitoring.Report)

setupPlatformSpecificMetrics()

return err
return nil
}

func setupPlatformSpecificMetrics() {
if runtime.GOOS != "windows" {
monitoring.NewFunc(systemMetrics, "load", reportSystemLoadAverage, monitoring.Report)
} else {
setupWindowsHandlesMetrics()
}

if runtime.GOOS == "linux" {
monitoring.NewFunc(beatMetrics, "fd", reportFDUsage, monitoring.Report)
}
setupLinuxBSDFDMetrics()
}

func reportMemStats(m monitoring.Mode, V monitoring.Visitor) {
Expand Down Expand Up @@ -214,62 +218,6 @@ func getCPUUsage() (float64, *process.Ticks, error) {
return totalCPUUsage, &p, nil
}

func reportFDUsage(_ monitoring.Mode, V monitoring.Visitor) {
V.OnRegistryStart()
defer V.OnRegistryFinished()

open, hardLimit, softLimit, err := getFDUsage()
if err != nil {
logp.Err("Error while retrieving FD information: %v", err)
return
}

monitoring.ReportInt(V, "open", int64(open))
monitoring.ReportNamespace(V, "limit", func() {
monitoring.ReportInt(V, "hard", int64(hardLimit))
monitoring.ReportInt(V, "soft", int64(softLimit))
})
}

func getFDUsage() (open, hardLimit, softLimit uint64, err error) {
state, err := getBeatProcessState()
if err != nil {
return 0, 0, 0, err
}

iOpen, err := state.GetValue("fd.open")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting number of open FD: %v", err)
}

open, ok := iOpen.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting value of open FDs to uint64: %v", iOpen)
}

iHardLimit, err := state.GetValue("fd.limit.hard")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting FD hard limit: %v", err)
}

hardLimit, ok = iHardLimit.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting values of FD hard limit: %v", iHardLimit)
}

iSoftLimit, err := state.GetValue("fd.limit.soft")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting FD hard limit: %v", err)
}

softLimit, ok = iSoftLimit.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting values of FD hard limit: %v", iSoftLimit)
}

return open, hardLimit, softLimit, nil
}

func reportSystemLoadAverage(_ monitoring.Mode, V monitoring.Visitor) {
V.OnRegistryStart()
defer V.OnRegistryFinished()
Expand Down
87 changes: 87 additions & 0 deletions libbeat/cmd/instance/metrics_file_descriptors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// +build linux freebsd,cgo

package instance

import (
"fmt"

"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/monitoring"
)

func setupLinuxBSDFDMetrics() {
monitoring.NewFunc(beatMetrics, "handles", reportFDUsage, monitoring.Report)
}

func reportFDUsage(_ monitoring.Mode, V monitoring.Visitor) {
V.OnRegistryStart()
defer V.OnRegistryFinished()

open, hardLimit, softLimit, err := getFDUsage()
if err != nil {
logp.Err("Error while retrieving FD information: %v", err)
return
}

monitoring.ReportInt(V, "open", int64(open))
monitoring.ReportNamespace(V, "limit", func() {
monitoring.ReportInt(V, "hard", int64(hardLimit))
monitoring.ReportInt(V, "soft", int64(softLimit))
})
}

func getFDUsage() (open, hardLimit, softLimit uint64, err error) {
state, err := getBeatProcessState()
if err != nil {
return 0, 0, 0, err
}

iOpen, err := state.GetValue("fd.open")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting number of open FD: %v", err)
}

open, ok := iOpen.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting value of open FDs to uint64: %v", iOpen)
}

iHardLimit, err := state.GetValue("fd.limit.hard")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting FD hard limit: %v", err)
}

hardLimit, ok = iHardLimit.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting values of FD hard limit: %v", iHardLimit)
}

iSoftLimit, err := state.GetValue("fd.limit.soft")
if err != nil {
return 0, 0, 0, fmt.Errorf("error getting FD hard limit: %v", err)
}

softLimit, ok = iSoftLimit.(uint64)
if !ok {
return 0, 0, 0, fmt.Errorf("error converting values of FD hard limit: %v", iSoftLimit)
}

return open, hardLimit, softLimit, nil
}
24 changes: 24 additions & 0 deletions libbeat/cmd/instance/metrics_file_descriptors_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// +build !linux
// +build !freebsd !cgo

package instance

// FDUsage is only supported on Linux and FreeBSD.
func setupLinuxBSDFDMetrics() {}
67 changes: 67 additions & 0 deletions libbeat/cmd/instance/metrics_handles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// +build windows

package instance

import (
"github.com/elastic/beats/libbeat/logp"
"github.com/elastic/beats/libbeat/monitoring"
sysinfo "github.com/elastic/go-sysinfo"
"github.com/elastic/go-sysinfo/types"
)

const (
fileHandlesNotReported = "Following metrics will not be reported: beat.handles.open"
)

var (
handleCounter types.OpenHandleCounter
)

func setupWindowsHandlesMetrics() {
beatProcessSysInfo, err := sysinfo.Self()
if err != nil {
logp.Err("Error while getting own process info: %v", err)
logp.Err(fileHandlesNotReported)
return
}

var ok bool
handleCounter, ok = beatProcessSysInfo.(types.OpenHandleCounter)
if !ok {
logp.Err("Process does not implement types.OpenHandleCounter: %v", beatProcessSysInfo)
logp.Err(fileHandlesNotReported)
return
}

monitoring.NewFunc(beatMetrics, "handles", reportOpenHandles, monitoring.Report)
}

func reportOpenHandles(_ monitoring.Mode, V monitoring.Visitor) {
V.OnRegistryStart()
defer V.OnRegistryFinished()

n, err := handleCounter.OpenHandleCount()
if err != nil {
logp.Err("Error while retrieving the number of open file handles: %v", err)
return
}

monitoring.ReportInt(V, "open", int64(n))
}
23 changes: 23 additions & 0 deletions libbeat/cmd/instance/metrics_handles_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// +build !windows

package instance

// Counting number of open handles is only supported on Windows.
func setupWindowsHandlesMetrics() {}
6 changes: 3 additions & 3 deletions libbeat/monitoring/report/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ var gauges = map[string]bool{
"beat.cpu.total.value": true,
"beat.cpu.total.ticks": true,
"beat.cpu.total.time": true,
"beat.fd.open": true,
"beat.fd.limit.hard": true,
"beat.fd.limit.soft": true,
"beat.handles.open": true,
"beat.handles.limit.hard": true,
"beat.handles.limit.soft": true,
"system.load.1": true,
"system.load.5": true,
"system.load.15": true,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0d28221

Please sign in to comment.