Skip to content

Commit

Permalink
[Ajat|Baskara] make StatsdCollector implementation simple
Browse files Browse the repository at this point in the history
  • Loading branch information
ajatprabha committed Jul 1, 2019
1 parent ec8eba0 commit eebb397
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 119 deletions.
40 changes: 0 additions & 40 deletions pkg/metrics/collector.go

This file was deleted.

15 changes: 0 additions & 15 deletions pkg/metrics/collector_test.go

This file was deleted.

17 changes: 17 additions & 0 deletions pkg/metrics/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package metrics

import "time"

type Type int

const (
Duration Type = 0
Guage Type = 1
)

type UpdateOption struct {
Name string
Type Type
NumValue float64
Duration time.Duration
}
64 changes: 26 additions & 38 deletions pkg/metrics/statsd_collector.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package metrics

import (
"fmt"
"github.com/cactus/go-statsd-client/statsd"
"***REMOVED***/darkroom/core/pkg/logger"
"time"
)

Expand All @@ -12,22 +14,12 @@ const (
GigabitStatsdFlushBytes = 8932
)

type StatsdCollector struct {
client statsd.Statter
processingDuration string
downloadDuration string
totalDuration string
readDuration string
writeDuration string
cropDuration string
resizeDuration string
monoDuration string
sampleRate float32
}
var instance *statsdClient

type StatsdCollectorClient struct {
client statsd.Statter
sampleRate float32
type statsdClient struct {
client statsd.Statter
collectorName string
sampleRate float32
}

// StatsdCollectorConfig provides configuration that the Statsd client will need.
Expand All @@ -42,7 +34,8 @@ type StatsdCollectorConfig struct {
FlushBytes int
}

func InitializeStatsdCollector(config *StatsdCollectorConfig) *StatsdCollectorClient {
// InitializeStatsdCollector will start publishing metrics in the form {config.Prefix}.{name}.{updateOption.Name}
func InitializeStatsdCollector(config *StatsdCollectorConfig, name string) error {
flushBytes := config.FlushBytes
if flushBytes == 0 {
flushBytes = LANStatsdFlushBytes
Expand All @@ -55,30 +48,25 @@ func InitializeStatsdCollector(config *StatsdCollectorConfig) *StatsdCollectorCl

c, _ := statsd.NewBufferedClient(config.StatsdAddr, config.Prefix, 1*time.Second, flushBytes)
// TODO Add logger for error
return &StatsdCollectorClient{client: c, sampleRate: sampleRate}
}

// NewStatsdMetricCollector creates a collector with specific name. The
// prefix given to these stats will be {config.Prefix}.{name}.{metric}.
func (s *StatsdCollectorClient) NewStatsdMetricCollector(name string) MetricCollector {
return &StatsdCollector{
client: s.client,
processingDuration: name + ".processingDuration",
downloadDuration: name + ".downloadDuration",
totalDuration: name + ".totalDuration",
readDuration: name + ".readDuration",
writeDuration: name + ".writeDuration",
cropDuration: name + ".cropDuration",
resizeDuration: name + ".resizeDuration",
monoDuration: name + ".monoDuration",
sampleRate: s.sampleRate,
}
instance = &statsdClient{client: c, collectorName: name, sampleRate: sampleRate}
return nil
}

func (sc *StatsdCollector) Update(MetricResult) {
// TODO ("implement me")
var formatter = func(on string) string {
return fmt.Sprintf("%s.%s", instance.collectorName, on)
}

func (sc *StatsdCollector) Reset() {
// TODO ("implement me")
func Update(updateOption UpdateOption) {
if instance == nil {
return
}
var err error
switch updateOption.Type {
case Duration:
err = instance.client.TimingDuration(formatter(updateOption.Name), updateOption.Duration, instance.sampleRate)
break
case Guage:
err = instance.client.Gauge(formatter(updateOption.Name), int64(updateOption.NumValue), instance.sampleRate)
}
logger.Error(err)
}
64 changes: 43 additions & 21 deletions pkg/metrics/statsd_collector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,56 @@ package metrics
import (
"github.com/cactus/go-statsd-client/statsd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"
"time"
)

func TestInitializeStatsdCollector(t *testing.T) {
scc := InitializeStatsdCollector(&StatsdCollectorConfig{})
assert.NotNil(t, scc)
// Test Statter client
err := InitializeStatsdCollector(&StatsdCollectorConfig{FlushBytes: 0}, "app-name")
assert.Nil(t, err)
assert.NotNil(t, instance)
assert.NotNil(t, instance.client)

// Test sampleRate
scc = InitializeStatsdCollector(&StatsdCollectorConfig{SampleRate: 5})
assert.NotNil(t, scc)
assert.Equal(t, float32(5), scc.sampleRate)

scc = InitializeStatsdCollector(&StatsdCollectorConfig{})
assert.NotNil(t, scc)
assert.Equal(t, float32(1), scc.sampleRate)
err = InitializeStatsdCollector(&StatsdCollectorConfig{SampleRate: 5}, "app-name")
assert.Nil(t, err)
assert.Equal(t, float32(5), instance.sampleRate)

// Test Statter client
scc = InitializeStatsdCollector(&StatsdCollectorConfig{FlushBytes: 0})
assert.NotNil(t, scc)
assert.NotNil(t, scc.client)
err = InitializeStatsdCollector(&StatsdCollectorConfig{}, "app-name")
assert.Nil(t, err)
assert.Equal(t, float32(1), instance.sampleRate)
}

func TestNewStatsdMetricCollector(t *testing.T) {
scc := InitializeStatsdCollector(&StatsdCollectorConfig{Prefix: "darkroom"})
mc := scc.NewStatsdMetricCollector("app-name")
assert.NotNil(t, mc)
func TestUpdate(t *testing.T) {
// Test when instance is nil
instance = nil
Update(UpdateOption{})

_ = InitializeStatsdCollector(&StatsdCollectorConfig{}, "app-name")

mc := &mockStatsdClient{}
instance.client = mc

now := time.Now()
mc.On("TimingDuration",
mock.AnythingOfType("string"),
mock.AnythingOfType("time.Duration"),
mock.AnythingOfType("float32")).Return(nil)
Update(UpdateOption{Type: Duration, Duration: time.Since(now)})

mc.On("Gauge",
mock.AnythingOfType("string"),
mock.AnythingOfType("int64"),
mock.AnythingOfType("float32")).Return(nil)
Update(UpdateOption{Type: Guage, NumValue: 500})

mc.AssertExpectations(t)
}

type mockStatsdClient struct {
mock.Mock
}

func (msc *mockStatsdClient) Inc(string, int64, float32) error {
Expand All @@ -43,8 +63,9 @@ func (msc *mockStatsdClient) Dec(string, int64, float32) error {
panic("implement me")
}

func (msc *mockStatsdClient) Gauge(string, int64, float32) error {
panic("implement me")
func (msc *mockStatsdClient) Gauge(str string, i int64, sr float32) error {
args := msc.Called(str, i, sr)
return args.Error(0)
}

func (msc *mockStatsdClient) GaugeDelta(string, int64, float32) error {
Expand All @@ -55,8 +76,9 @@ func (msc *mockStatsdClient) Timing(string, int64, float32) error {
panic("implement me")
}

func (msc *mockStatsdClient) TimingDuration(string, time.Duration, float32) error {
panic("implement me")
func (msc *mockStatsdClient) TimingDuration(str string, t time.Duration, sr float32) error {
args := msc.Called(str, t, sr)
return args.Error(0)
}

func (msc *mockStatsdClient) Set(string, string, float32) error {
Expand Down
32 changes: 27 additions & 5 deletions pkg/processor/native/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (
"image/draw"
"image/jpeg"
"image/png"
"***REMOVED***/darkroom/core/pkg/metrics"
"***REMOVED***/darkroom/core/pkg/processor"
"time"
)

const (
Expand All @@ -23,23 +25,25 @@ type BildProcessor struct {
}

func (bp *BildProcessor) Crop(input []byte, width, height int, point processor.CropPoint) ([]byte, error) {
img, f, err := image.Decode(bytes.NewReader(input))
img, f, err := bp.decode(input)
if err != nil {
return nil, err
}

w, h := getResizeWidthAndHeightForCrop(width, height, img.Bounds().Dx(), img.Bounds().Dy())

t := time.Now()
img = transform.Resize(img, w, h, transform.Linear)
x0, y0 := getStartingPointForCrop(w, h, width, height, point)
rect := image.Rect(x0, y0, width+x0, height+y0)
img = (clone.AsRGBA(img)).SubImage(rect)
metrics.Update(metrics.UpdateOption{Name: "cropDuration", Type: metrics.Duration, Duration: time.Since(t)})

return bp.encode(img, f)
}

func (bp *BildProcessor) Resize(input []byte, width, height int) ([]byte, error) {
img, f, err := image.Decode(bytes.NewReader(input))
img, f, err := bp.decode(input)
if err != nil {
return nil, err
}
Expand All @@ -49,22 +53,25 @@ func (bp *BildProcessor) Resize(input []byte, width, height int) ([]byte, error)

w, h := getResizeWidthAndHeight(width, height, initW, initH)
if w != initW || h != initH {
t := time.Now()
img = transform.Resize(img, w, h, transform.Linear)
metrics.Update(metrics.UpdateOption{Name: "resizeDuration", Type: metrics.Duration, Duration: time.Since(t)})
}

return bp.encode(img, f)
}

func (bp *BildProcessor) Watermark(base []byte, overlay []byte, opacity uint8) ([]byte, error) {
baseImg, f, err := image.Decode(bytes.NewReader(base))
baseImg, f, err := bp.decode(base)
if err != nil {
return nil, err
}
overlayImg, _, err := image.Decode(bytes.NewReader(overlay))
overlayImg, _, err := bp.decode(overlay)
if err != nil {
return nil, err
}

t := time.Now()
ratio := float64(overlayImg.Bounds().Dy()) / float64(overlayImg.Bounds().Dx())
dWidth := float64(baseImg.Bounds().Dx()) / 2.0

Expand All @@ -81,21 +88,35 @@ func (bp *BildProcessor) Watermark(base []byte, overlay []byte, opacity uint8) (

// Performing overlay
draw.DrawMask(baseImg.(draw.Image), overlayImg.Bounds().Add(offset), overlayImg, image.ZP, mask, image.ZP, draw.Over)
metrics.Update(metrics.UpdateOption{Name: "watermarkDuration", Type: metrics.Duration, Duration: time.Since(t)})

return bp.encode(baseImg, f)
}

func (bp *BildProcessor) GrayScale(input []byte) ([]byte, error) {
img, f, err := image.Decode(bytes.NewReader(input))
img, f, err := bp.decode(input)
if err != nil {
return nil, err
}

t := time.Now()
img = effect.Grayscale(img)
metrics.Update(metrics.UpdateOption{Name: "grayScaleDuration", Type: metrics.Duration, Duration: time.Since(t)})

return bp.encode(img, f)
}

func (bp *BildProcessor) decode(data []byte) (image.Image, string, error) {
t := time.Now()
img, f, err := image.Decode(bytes.NewReader(data))
if err == nil {
metrics.Update(metrics.UpdateOption{Name: "decodeDuration", Type: metrics.Duration, Duration: time.Since(t)})
}
return img, f, err
}

func (bp *BildProcessor) encode(img image.Image, format string) ([]byte, error) {
t := time.Now()
if format == pngType && isOpaque(img) {
format = jpgType
}
Expand All @@ -107,6 +128,7 @@ func (bp *BildProcessor) encode(img image.Image, format string) ([]byte, error)
} else {
err = jpeg.Encode(buff, img, nil)
}
metrics.Update(metrics.UpdateOption{Name: "encodeDuration", Type: metrics.Duration, Duration: time.Since(t)})
return buff.Bytes(), err
}

Expand Down

0 comments on commit eebb397

Please sign in to comment.