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

EMF as Map #12

Open
wants to merge 4 commits into
base: master
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
7 changes: 4 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: set up Go 1.x
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: ^1.14
go-version: '1.20'
check-latest: true
id: go
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: modules
run: go mod download
- name: test
Expand Down
31 changes: 21 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# aws-embedded-metrics-golang

![test](https://github.com/prozz/aws-embedded-metrics-golang/workflows/test/badge.svg?branch=master)
![golangci-lint](https://github.com/prozz/aws-embedded-metrics-golang/workflows/lint/badge.svg?branch=master)
![test](https://github.com/gaeste/aws-embedded-metrics-golang/workflows/test/badge.svg?branch=master)
![golangci-lint](https://github.com/gaeste/aws-embedded-metrics-golang/workflows/lint/badge.svg?branch=master)

Go implementation of AWS CloudWatch [Embedded Metric Format](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html)
Go implementation of [AWS CloudWatch Embedded Metric Format](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html).

It's aim is to simplify reporting metrics to CloudWatch:
The aim is to simplify reporting metrics to CloudWatch:

- using EMF avoids additional HTTP API calls to CloudWatch as metrics are logged in JSON format to stdout
- no need for additional dependencies in your services (or mocks in tests) to report metrics from inside your code
Expand All @@ -16,21 +16,32 @@ Supports namespaces, setting dimensions and properties as well as different cont
## Installation

```shell
go get github.com/prozz/aws-embedded-metrics-golang
go get github.com/gaeste/aws-embedded-metrics-golang
```

## Usage

```
emf.New().Namespace("mtg").Metric("totalWins", 1500).Log()

emf.New().Dimension("colour", "red").
MetricAs("gameLength", 2, emf.Seconds).Log()
emf.New().
Dimension("colour", "red").
MetricAs("gameLength", 2, emf.Seconds).
Log()

emf.New().DimensionSet(
emf.New().
DimensionSet(
emf.NewDimension("format", "edh"),
emf.NewDimension("commander", "Muldrotha")).
MetricAs("wins", 1499, emf.Count).Log()
emf.NewDimension("commander", "Muldrotha")
).
MetricAs("wins", 1499, emf.Count).
Log()

metrics := emf.New().
Namespace("mtg").
Metric("totalWins", 1500).
Build()
log.With(metrics).Info("log message with metrics attached as named value pair")
```

You may also use the lib together with `defer`.
Expand Down
22 changes: 19 additions & 3 deletions emf/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,22 @@ func (l *Logger) MetricsFloatAs(m map[string]float64, unit MetricUnit) *Logger {

// Log prints all Contexts and metric values to chosen output in Embedded Metric Format.
func (l *Logger) Log() {
emf := l.build()
if len(emf) == 0 {
return
}

buf, _ := json.Marshal(emf)
_, _ = fmt.Fprintln(l.out, string(buf))
}

// Build constructs the EMF structure as a map that includes all Contexts and metric values.
// The map is arranged according to Embedded Metric Format.
func (l *Logger) Build() map[string]interface{} {
return l.build()
}

func (l *Logger) build() map[string]interface{} {
var metrics []MetricDirective
if len(l.defaultContext.metricDirective.Metrics) > 0 {
metrics = append(metrics, l.defaultContext.metricDirective)
Expand All @@ -206,16 +222,16 @@ func (l *Logger) Log() {
}

if len(metrics) == 0 {
return
return map[string]interface{}{}
}

l.values["_aws"] = Metadata{
Timestamp: l.timestamp,
Metrics: metrics,
LogGroupName: l.logGroupName,
}
buf, _ := json.Marshal(l.values)
_, _ = fmt.Fprintln(l.out, string(buf))

return l.values
}

// NewContext creates new context for given logger.
Expand Down
29 changes: 26 additions & 3 deletions emf/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package emf_test

import (
"bytes"
"io/ioutil"
"encoding/json"
"os"
"testing"

"github.com/gaeste/aws-embedded-metrics-golang/emf"
"github.com/kinbiko/jsonassert"
"github.com/prozz/aws-embedded-metrics-golang/emf"
)

func TestEmf(t *testing.T) {
Expand Down Expand Up @@ -219,12 +219,17 @@ func TestEmf(t *testing.T) {
logger.Log()

println(buf.String())
f, err := ioutil.ReadFile(tc.expected)
f, err := os.ReadFile(tc.expected)
if err != nil {
t.Fatal("unable to read file with expected json")
}

jsonassert.New(t).Assertf(buf.String(), string(f))

// test Build() to generate the same structure as Log()
metrics := logger.Build()
metricsJson, _ := json.Marshal(metrics)
jsonassert.New(t).Assertf(string(metricsJson), string(f))
})
}

Expand All @@ -238,6 +243,14 @@ func TestEmf(t *testing.T) {
}
})

t.Run("no metrics set, with build", func(t *testing.T) {
m := emf.New().Build()

if len(m) > 0 {
t.Error("Map not empty")
}
})

t.Run("new context, no metrics set", func(t *testing.T) {
var buf bytes.Buffer
logger := emf.New(emf.WithWriter(&buf))
Expand All @@ -248,6 +261,16 @@ func TestEmf(t *testing.T) {
t.Error("Buffer not empty")
}
})

t.Run("new context, no metrics set, with build", func(t *testing.T) {
logger := emf.New()
logger.NewContext().Namespace("galaxy")
m := logger.Build()

if len(m) > 0 {
t.Error("Map not empty")
}
})
}

func setenv(t *testing.T, env map[string]string) {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/prozz/aws-embedded-metrics-golang
module github.com/gaeste/aws-embedded-metrics-golang

go 1.14
go 1.20

require github.com/kinbiko/jsonassert v1.1.1