Skip to content

Commit

Permalink
Lightstep fork of go-contrib instrumentation/host metrics (#268)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmacd authored Sep 7, 2022
1 parent 0615899 commit ff08f66
Show file tree
Hide file tree
Showing 5 changed files with 511 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
in some cases negative values. These will be handled no more than
once per 30 seconds per condition.
[#272](https://github.com/lightstep/otel-launcher-go/pull/272)
- Reduced replacement for go-contrib instrumentation/host added as
lightstep/instrumentation/host; same code but removes process metrics
[#268](https://github.com/lightstep/otel-launcher-go/pull/268)

## [1.10.1](https://github.com/lightstep/otel-launcher-go/releases/tag/v1.10.1) - 2022-08-29

### Changed

- Revert the default change of temporality to "cumulative" from #258.
New users are recommended to configure
`WithMetricExporterTemporalityPreference("stateless")` temporality
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/lightstep/otel-launcher-go/lightstep/sdk/metric v1.10.1
github.com/lightstep/otel-launcher-go/pipelines v1.10.1
github.com/sethvargo/go-envconfig v0.8.2
github.com/shirou/gopsutil/v3 v3.22.6
github.com/stretchr/testify v1.8.0
go.opentelemetry.io/otel v1.9.0
go.opentelemetry.io/otel/metric v0.31.0
Expand All @@ -27,7 +28,6 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shirou/gopsutil/v3 v3.22.6 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
Expand Down
36 changes: 36 additions & 0 deletions lightstep/instrumentation/host/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright The OpenTelemetry Authors
//
// Licensed 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.

// Package host provides the conventional host metrics
// specified by OpenTelemetry. Host metric events are sometimes
// collected through the OpenTelemetry Collector "hostmetrics"
// receiver running as an agent; this instrumentation is an
// alternative for processes that want to record the same information
// without an agent.
//
// The metric events produced are listed here with attribute dimensions.
//
// Name Attribute
//
// ----------------------------------------------------------------------
//
// system.cpu.time state=user|system|other|idle
// system.memory.usage state=used|available
// system.memory.utilization state=used|available
// system.network.io direction=transmit|receive
//
// See https://github.com/open-telemetry/oteps/blob/main/text/0119-standard-system-metrics.md
// for the definition of these metric instruments.

package host // import "github.com/lightstep/otel-launcher-go/lightstep/instrumentation/host"
242 changes: 242 additions & 0 deletions lightstep/instrumentation/host/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// Copyright The OpenTelemetry Authors
//
// Licensed 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.

package host // import "github.com/lightstep/otel-launcher-go/lightstep/instrumentation/host"

import (
"context"
"fmt"
"sync"

"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"

"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/global"
"go.opentelemetry.io/otel/metric/instrument"
"go.opentelemetry.io/otel/metric/instrument/asyncfloat64"
"go.opentelemetry.io/otel/metric/instrument/asyncint64"
"go.opentelemetry.io/otel/metric/unit"
)

// Host reports the work-in-progress conventional host metrics specified by OpenTelemetry.
type host struct {
meter metric.Meter
}

// config contains optional settings for reporting host metrics.
type config struct {
// MeterProvider sets the metric.MeterProvider. If nil, the global
// Provider will be used.
MeterProvider metric.MeterProvider
}

// Option supports configuring optional settings for host metrics.
type Option interface {
apply(*config)
}

// WithMeterProvider sets the Metric implementation to use for
// reporting. If this option is not used, the global metric.MeterProvider
// will be used. `provider` must be non-nil.
func WithMeterProvider(provider metric.MeterProvider) Option {
return metricProviderOption{provider}
}

type metricProviderOption struct{ metric.MeterProvider }

func (o metricProviderOption) apply(c *config) {
if o.MeterProvider != nil {
c.MeterProvider = o.MeterProvider
}
}

// Attribute sets.
var (
// Attribute sets for CPU time measurements.

AttributeCPUTimeUser = []attribute.KeyValue{attribute.String("state", "user")}
AttributeCPUTimeSystem = []attribute.KeyValue{attribute.String("state", "system")}
AttributeCPUTimeOther = []attribute.KeyValue{attribute.String("state", "other")}
AttributeCPUTimeIdle = []attribute.KeyValue{attribute.String("state", "idle")}

// Attribute sets used for Memory measurements.

AttributeMemoryAvailable = []attribute.KeyValue{attribute.String("state", "available")}
AttributeMemoryUsed = []attribute.KeyValue{attribute.String("state", "used")}

// Attribute sets used for Network measurements.

AttributeNetworkTransmit = []attribute.KeyValue{attribute.String("direction", "transmit")}
AttributeNetworkReceive = []attribute.KeyValue{attribute.String("direction", "receive")}
)

// newConfig computes a config from a list of Options.
func newConfig(opts ...Option) config {
c := config{
MeterProvider: global.MeterProvider(),
}
for _, opt := range opts {
opt.apply(&c)
}
return c
}

// Start initializes reporting of host metrics using the supplied config.
func Start(opts ...Option) error {
c := newConfig(opts...)
if c.MeterProvider == nil {
c.MeterProvider = global.MeterProvider()
}
h := newHost(c)
return h.register()
}

func newHost(c config) *host {
return &host{
meter: c.MeterProvider.Meter("otel_launcher_go/host"),
}
}

func (h *host) register() error {
var (
err error

hostCPUTime asyncfloat64.Counter
hostMemoryUsage asyncint64.UpDownCounter
hostMemoryUtilization asyncfloat64.Gauge
networkIOUsage asyncint64.Counter

// lock prevents a race between batch observer and instrument registration.
lock sync.Mutex
)

lock.Lock()
defer lock.Unlock()

if hostCPUTime, err = h.meter.AsyncFloat64().Counter(
"system.cpu.time",
instrument.WithUnit("s"),
instrument.WithDescription(
"Accumulated CPU time spent by this process host attributed by state (User, System, Other, Idle)",
),
); err != nil {
return err
}

if hostMemoryUsage, err = h.meter.AsyncInt64().UpDownCounter(
"system.memory.usage",
instrument.WithUnit(unit.Bytes),
instrument.WithDescription(
"Memory usage of this process host attributed by memory state (Used, Available)",
),
); err != nil {
return err
}

if hostMemoryUtilization, err = h.meter.AsyncFloat64().Gauge(
"system.memory.utilization",
instrument.WithDescription(
"Memory utilization of this process host attributed by memory state (Used, Available)",
),
); err != nil {
return err
}

if networkIOUsage, err = h.meter.AsyncInt64().Counter(
"system.network.io",
instrument.WithUnit(unit.Bytes),
instrument.WithDescription(
"Bytes transferred attributed by direction (Transmit, Receive)",
),
); err != nil {
return err
}

err = h.meter.RegisterCallback(
[]instrument.Asynchronous{
hostCPUTime,
hostMemoryUsage,
hostMemoryUtilization,
networkIOUsage,
},
func(ctx context.Context) {
lock.Lock()
defer lock.Unlock()

hostTimeSlice, err := cpu.TimesWithContext(ctx, false)
if err != nil {
otel.Handle(err)
return
}
if len(hostTimeSlice) != 1 {
otel.Handle(fmt.Errorf("host CPU usage: incorrect summary count"))
return
}

vmStats, err := mem.VirtualMemoryWithContext(ctx)
if err != nil {
otel.Handle(err)
return
}

ioStats, err := net.IOCountersWithContext(ctx, false)
if err != nil {
otel.Handle(err)
return
}
if len(ioStats) != 1 {
otel.Handle(fmt.Errorf("host network usage: incorrect summary count"))
return
}

// Host CPU time
hostTime := hostTimeSlice[0]
hostCPUTime.Observe(ctx, hostTime.User, AttributeCPUTimeUser...)
hostCPUTime.Observe(ctx, hostTime.System, AttributeCPUTimeSystem...)

// Note: "other" is the sum of all other known states.
other := hostTime.Nice +
hostTime.Iowait +
hostTime.Irq +
hostTime.Softirq +
hostTime.Steal +
hostTime.Guest +
hostTime.GuestNice

hostCPUTime.Observe(ctx, other, AttributeCPUTimeOther...)
hostCPUTime.Observe(ctx, hostTime.Idle, AttributeCPUTimeIdle...)

// Host memory usage
hostMemoryUsage.Observe(ctx, int64(vmStats.Used), AttributeMemoryUsed...)
hostMemoryUsage.Observe(ctx, int64(vmStats.Available), AttributeMemoryAvailable...)

// Host memory utilization
hostMemoryUtilization.Observe(ctx, float64(vmStats.Used)/float64(vmStats.Total), AttributeMemoryUsed...)
hostMemoryUtilization.Observe(ctx, float64(vmStats.Available)/float64(vmStats.Total), AttributeMemoryAvailable...)

// Host network usage
networkIOUsage.Observe(ctx, int64(ioStats[0].BytesSent), AttributeNetworkTransmit...)
networkIOUsage.Observe(ctx, int64(ioStats[0].BytesRecv), AttributeNetworkReceive...)
})

if err != nil {
return err
}

return nil
}
Loading

0 comments on commit ff08f66

Please sign in to comment.