Skip to content

Commit

Permalink
Implement InstrumentedCall (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
proflayton authored and robskillington committed Sep 5, 2017
1 parent 2605c40 commit 3dd04ef
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
60 changes: 60 additions & 0 deletions instrument.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tally

const (
_resultType = "result_type"
_resultTypeError = "error"
_resultTypeSuccess = "success"
_timingFormat = "latency"
)

// NewInstrumentedCall returns an InstrumentedCall with the given name
func NewInstrumentedCall(scope Scope, name string) InstrumentedCall {
return &instrumentedCall{
error: scope.Tagged(map[string]string{_resultType: _resultTypeError}).Counter(name),
success: scope.Tagged(map[string]string{_resultType: _resultTypeSuccess}).Counter(name),
timing: scope.SubScope(name).Timer(_timingFormat),
}
}

type instrumentedCall struct {
scope Scope
success Counter
error Counter
timing Timer
}

// Exec executes the given block of code, and records whether it succeeded or
// failed, and the amount of time that it took
func (c *instrumentedCall) Exec(f ExecFn) error {
sw := c.timing.Start()

if err := f(); err != nil {
c.error.Inc(1.0)
return err
}

sw.Stop()
c.success.Inc(1.0)

return nil
}
38 changes: 38 additions & 0 deletions scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package tally

import (
"errors"
"math"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -313,6 +314,43 @@ func TestWriteReportLoop(t *testing.T) {
r.WaitAll()
}

func TestInstrumentedCallSuccess(t *testing.T) {
r := newTestStatsReporter()
s, closer := NewRootScope(ScopeOptions{Reporter: r}, 10)
defer closer.Close()

r.cg.Add(1)
r.tg.Add(1)
err := NewInstrumentedCall(s, "test_instrumented").Exec(func() error {
return nil
})
assert.Nil(t, err)

r.WaitAll()
assert.EqualValues(t, 1, r.counters["test_instrumented"].val)
resultType, _ := r.counters["test_instrumented"].tags["result_type"]
assert.EqualValues(t, resultType, "success")
assert.NotNil(t, r.timers["test_instrumented.latency"].val)
}

func TestInstrumentedCallFail(t *testing.T) {
r := newTestStatsReporter()
s, closer := NewRootScope(ScopeOptions{Reporter: r}, 10)
defer closer.Close()

r.cg.Add(1)
err := NewInstrumentedCall(s, "test_instrumented").Exec(func() error {
return errors.New("failure")
})
assert.NotNil(t, err)

r.WaitAll()
assert.EqualValues(t, 1, r.counters["test_instrumented"].val)
resultType, _ := r.counters["test_instrumented"].tags["result_type"]
assert.EqualValues(t, resultType, "error")
assert.Nil(t, r.timers["test_instrumented.latency"])
}

func TestCachedReportLoop(t *testing.T) {
r := newTestStatsReporter()
s, closer := NewRootScope(ScopeOptions{CachedReporter: r}, 10)
Expand Down
10 changes: 10 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,13 @@ type Capabilities interface {
// Tagging returns whether the reporter has the capability for tagged metrics.
Tagging() bool
}

// ExecFn will be executed in an InstrumentedCall
type ExecFn func() error

// An InstrumentedCall allows tracking the success, errors, and timing of blocks of code
type InstrumentedCall interface {
// Exec executes the given block of code, and records whether it succeeded or
// failed, and the amount of time that it took
Exec(f ExecFn) error
}

0 comments on commit 3dd04ef

Please sign in to comment.