alexandre-normand/slackscot

View on GitHub
opentelemetry.template

Summary

Maintainability
Test Coverage
import (
  "time"
  "fmt"
  "go.opentelemetry.io/otel/metric"
  "go.opentelemetry.io/otel/label"
)

{{ $decorator := (or .Vars.DecoratorName (printf "%sWithTelemetry" .Interface.Name)) }}

// {{$decorator}} implements {{.Interface.Type}} interface with all methods wrapped
// with open telemetry metrics
type {{$decorator}} struct {
  base                     {{.Interface.Type}}
  methodCounters           map[string]metric.BoundInt64Counter
  errCounters              map[string]metric.BoundInt64Counter
  methodTimeValueRecorders map[string]metric.BoundInt64ValueRecorder
}

// New{{.Interface.Name}}WithTelemetry returns an instance of the {{.Interface.Type}} decorated with open telemetry timing and count metrics
func New{{$decorator}}(base {{.Interface.Type}}, name string, meter metric.Meter) {{$decorator}} {
  return {{$decorator}} {
    base: base,
    methodCounters: new{{.Interface.Name}}MethodCounters("Calls", name, meter),
    errCounters: new{{.Interface.Name}}MethodCounters("Errors", name, meter),
    methodTimeValueRecorders: new{{.Interface.Name}}MethodTimeValueRecorders(name, meter),
  }
}

func new{{.Interface.Name}}MethodTimeValueRecorders(appName string, meter metric.Meter) (boundTimeValueRecorders map[string]metric.BoundInt64ValueRecorder) {
  boundTimeValueRecorders = make(map[string]metric.BoundInt64ValueRecorder)
  mt := metric.Must(meter)

  {{ $ifaceName := .Interface.Name }}
  {{range $method := .Interface.Methods}}
  n{{$method.Name}}ValRecorder := []rune("{{printf "%s_%s_ProcessingTimeMillis" $ifaceName $method.Name}}")
  n{{$method.Name}}ValRecorder[0] = unicode.ToLower(n{{$method.Name}}ValRecorder[0])
  m{{$method.Name}} := mt.NewInt64ValueRecorder(string(n{{$method.Name}}ValRecorder))
  boundTimeValueRecorders["{{$method.Name}}"] = m{{$method.Name}}.Bind(label.String("name", appName))
  {{end}}

  return boundTimeValueRecorders
}

func new{{.Interface.Name}}MethodCounters(suffix string, appName string, meter metric.Meter) (boundCounters map[string]metric.BoundInt64Counter) {
  boundCounters = make(map[string]metric.BoundInt64Counter)
  mt := metric.Must(meter)

  {{ $ifaceName := .Interface.Name }}
  {{range $method := .Interface.Methods}}
  n{{$method.Name}}Counter := []rune("{{printf "%s_%s_" $ifaceName $method.Name}}" + suffix)
  n{{$method.Name}}Counter[0] = unicode.ToLower(n{{$method.Name}}Counter[0])
  c{{$method.Name}} := mt.NewInt64Counter(string(n{{$method.Name}}Counter))
  boundCounters["{{$method.Name}}"] = c{{$method.Name}}.Bind(label.String("name", appName))
  {{end}}

  return boundCounters
}

{{range $method := .Interface.Methods}}
  // {{$method.Name}} implements {{$.Interface.Type}}
  func (_d {{$decorator}}) {{$method.Declaration}} {
      _since := time.Now()
      defer func() {
        {{- if $method.ReturnsError}}
          if err != nil {
            errCounter := _d.errCounters["{{$method.Name}}"]
            errCounter.Add(context.Background(), 1)
          }
        {{end}}

        methodCounter := _d.methodCounters["{{$method.Name}}"]
        methodCounter.Add(context.Background(), 1)

        methodTimeMeasure := _d.methodTimeValueRecorders["{{$method.Name}}"]
        methodTimeMeasure.Record(context.Background(), time.Since(_since).Milliseconds())
      }()
    {{$method.Pass "_d.base."}}
  }
{{end}}