Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Improved metrics setup #434

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/coopnorge/go-datadog-lib/v2/internal"
"github.com/coopnorge/go-datadog-lib/v2/metrics"
datadogLogger "github.com/coopnorge/go-logger/adapter/datadog"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
Expand Down Expand Up @@ -69,6 +70,11 @@ func start(options *options) error {
if err != nil {
return err
}
metricOptions := append([]metrics.Option{metrics.WithErrorHandler(options.errorHandler)}, options.metricOptions...)
err = metrics.GlobalSetup(metricOptions...)
if err != nil {
return err
}
return nil
}

Expand Down Expand Up @@ -99,5 +105,5 @@ func startProfiler(options *options) error {
func stop() error {
tracer.Stop()
profiler.Stop()
return nil
return metrics.Flush()
}
68 changes: 33 additions & 35 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,51 +532,49 @@ func main() {
}
```

## Metric - Datadog StatsD
## Metrics

Datadog supports custom metrics that you can utilize depending on the
application.
The package `github.com/coopnorge/go-datadog-lib/v2/metrics` contains function
to send telemetry. Before sending metrics `go-datadog-lib` has to be
[initialized](#application-setup).

For example, you could use it to track value of cart in side e-commerce shop.
Use cases:

Or you could register events for auth attempts.

All depends on the case and what you're looking forward to achieve.

### How to use StatsD in Go - Datadog

There is created an abstract client that simply connects to Datadog StatsD
service.

Also, you will already implement simple metric the collector that you can
extend or just use it to send your events and measurements.

### How to initialize Go - Datadog

To prepare the configuration you need to look at the `Setup` section. After
that you can create new instance of Datadog client for DD StatsD.
- track value of carts in an e-commerce shop.
- count failed authentication attempts.

```go
package your_pkg
package main

import (
"github.com/coopnorge/go-datadog-lib/v2/config"
"github.com/coopnorge/go-datadog-lib/v2/metric"
"context"

coopdatadog "github.com/coopnorge/go-datadog-lib/v2"
"github.com/coopnorge/go-datadog-lib/v2/metrics"
)

func MyServiceContainer(ddCfg *config.DatadogConfig) error {
// After that you will have pure DD StatsD client
ddClient := metrics.NewDatadogMetrics(ddCfg)
func main() {
err := run()
if err != nil {
panic(err)
}
}

// If you need simple metric collector then create
ddMetricCollector, ddMetricCollectorErr := metrics.NewBaseMetricCollector(ddClient)
if ddMetricCollectorErr != nil {
// Handle error / log error
}
// ddMetricCollector -> *BaseMetricCollector allows you to send metrics to Datadog

// ensure the metrics are sent before the program is terminated
defer ddMetricCollector.GracefulShutdown()
func run() error {
stop, err := coopdatadog.Start(context.Background())
if err != nil {
return err
}
defer func() {
err := stop()
if err != nil {
panic(err)
}
}()

metrics.Incr("my-metric")

return nil
}
```

Expand Down
2 changes: 2 additions & 0 deletions metric/datadog.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type (
)

// NewDatadogMetrics instance required to have cfg config.DatadogParameters to get information about service and optional orgPrefix to append into for metric name
//
// Deprecated: Use coopdatadog.Start() instead.
func NewDatadogMetrics(cfg config.DatadogParameters, orgPrefix string) (*DatadogMetrics, error) {
var ddClient *statsd.Client
var ddClientErr error
Expand Down
5 changes: 5 additions & 0 deletions metric/metric.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Package metric implements custom metrics with Dogstatsd
//
// Deprecated: use metrics instead
package metric

import (
Expand Down Expand Up @@ -51,6 +54,8 @@ func NewBaseMetricCollector(dm *DatadogMetrics) *BaseMetricCollector {
}

// AddMetric related to name with given value
//
// Deprecated: Use functions from the metrics package
func (m BaseMetricCollector) AddMetric(ctx context.Context, d Data) {
if m.DatadogMetrics == nil || m.DatadogMetrics.GetClient() == nil {
return
Expand Down
32 changes: 32 additions & 0 deletions metrics/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package metrics_test

import (
"context"

coopdatadog "github.com/coopnorge/go-datadog-lib/v2"
"github.com/coopnorge/go-datadog-lib/v2/metrics"
)

func Example() {
err := run()
if err != nil {
panic(err)
}
}

func run() error {
stop, err := coopdatadog.Start(context.Background())
if err != nil {
return err
}
defer func() {
err := stop()
if err != nil {
panic(err)
}
}()

metrics.Incr("my-metric")

return nil
}
122 changes: 122 additions & 0 deletions metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Package metrics implements custom metrics with Dogstatsd
package metrics

import (
"fmt"
"sync"
"time"

"github.com/DataDog/datadog-go/v5/statsd"
"github.com/coopnorge/go-datadog-lib/v2/errors"
"github.com/coopnorge/go-datadog-lib/v2/internal"
)

var (
setupOnce sync.Once
setupErr error
statsdClient statsd.ClientInterface
errorHandler errors.ErrorHandler
opts *options
)

// GlobalSetup configures the Dogstatsd Client. GlobalSetup is intended to be
// called from coopdatadog.Start(), but can be called directly.
func GlobalSetup(options ...Option) error {
setupOnce.Do(func() {
if internal.IsDatadogDisabled() {
statsdClient = &noopClient{}
return
}

opts, setupErr = resolveOptions(options)
if setupErr != nil {
return
}

statsdClient, setupErr = statsd.New(opts.dsdEndpoint, statsd.WithTags(opts.tags))
if setupErr != nil {
return
}
})
return setupErr
}

// Flush forces a flush of all the queued dogstatsd payloads.
func Flush() error {
err := statsdClient.Flush()
if err != nil {
return fmt.Errorf("failed to flush: %w", err)
}
return nil
}

// Gauge measures the value of a metric at a particular time.
func Gauge(name string, value float64, tags ...string) {
err := statsdClient.Gauge(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to send Gauge: %w", err))
}
}

// Count tracks how many times something happened per second.
func Count(name string, value int64, tags ...string) {
err := statsdClient.Count(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to to send Count: %w", err))
}
}

// Histogram tracks the statistical distribution of a set of values on each host.
func Histogram(name string, value float64, tags ...string) {
err := statsdClient.Histogram(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to to send Histogram: %w", err))
}
}

// Distribution tracks the statistical distribution of a set of values across your infrastructure.
func Distribution(name string, value float64, tags ...string) {
err := statsdClient.Distribution(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to to send Distribution: %w", err))
}
}

// Decr is just Count of -1
func Decr(name string, tags ...string) {
Count(name, -1, tags...)
}

// Incr is just Count of 1
func Incr(name string, tags ...string) {
Count(name, 1, tags...)
}

// Set counts the number of unique elements in a group.
func Set(name string, value string, tags ...string) {
err := statsdClient.Set(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to to send Set: %w", err))
}
}

// Timing sends timing information, it is an alias for TimeInMilliseconds
func Timing(name string, value time.Duration, tags ...string) {
TimeInMilliseconds(name, value.Seconds()*1000, tags...)
}

// TimeInMilliseconds sends timing information in milliseconds.
func TimeInMilliseconds(name string, value float64, tags ...string) {
err := statsdClient.TimeInMilliseconds(name, value, tags, opts.metricSampleRate)
if err != nil {
errorHandler(fmt.Errorf("failed to to send TimeInMilliseconds: %w", err))
}
}

// SimpleEvent sends an event with the provided title and text.
func SimpleEvent(title, text string) {
err := statsdClient.SimpleEvent(title, text)
if err != nil {
errorHandler(fmt.Errorf("failed to send Event: %w", err))
}
}
Loading
Loading