Skip to content

Commit

Permalink
Add internal domain model (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
yurishkuro authored Dec 9, 2016
1 parent e3d6a8e commit c285f2d
Show file tree
Hide file tree
Showing 13 changed files with 551 additions and 5 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ test-and-lint: test fmt lint

.PHONY: go-gen
go-gen:
go generate ./...
go generate $(PACKAGES)

.PHONY: clean
clean:
rm -rf cover.out cover.html lint.log fmt.log

.PHONY: test
test: go-gen
Expand Down
20 changes: 16 additions & 4 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions model/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2016 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 model describes the internal data model for Trace and Span
package model
152 changes: 152 additions & 0 deletions model/keyvalue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (c) 2016 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 model

import "math"

// ValueType describes the type of value contained in a KeyValue struct
type ValueType int

const (
// StringType indicates the value is a unicode string
StringType ValueType = iota
// BoolType indicates the value is a Boolean encoded as int64 number 0 or 1
BoolType
// Int64Type indicates the value is an int64 number
Int64Type
// Float64Type indicates the value is a float64 number stored as int64
Float64Type
// BinaryType indicates the value is binary blob stored as a byte array
BinaryType
)

// KeyValue describes a tag or a log field that consists of a key and a typed value.
// Before accessing a value, the caller must check the type. Boolean and numeric
// values should be accessed via accessor methods Bool(), Int64(), and Float64().
//
// This struct is designed to minimize heap allocations.
type KeyValue struct {
Key string `json:"key"`
VType ValueType `json:"vType"`
VStr string `json:"vStr,omitempty"`
VNum int64 `json:"vNum,omitempty"`
VBlob []byte `json:"vBlob,omitempty"`
}

// String creates a String-typed KeyValue
func String(key string, value string) KeyValue {
return KeyValue{Key: key, VType: StringType, VStr: value}
}

// Bool creates a Bool-typed KeyValue
func Bool(key string, value bool) KeyValue {
var val int64
if value {
val = 1
}
return KeyValue{Key: key, VType: BoolType, VNum: val}
}

// Int64 creates a Int64-typed KeyValue
func Int64(key string, value int64) KeyValue {
return KeyValue{Key: key, VType: Int64Type, VNum: value}
}

// Float64 creates a Float64-typed KeyValue
func Float64(key string, value float64) KeyValue {
return KeyValue{Key: key, VType: Float64Type, VNum: int64(math.Float64bits(value))}
}

// Binary creates a Binary-typed KeyValue
func Binary(key string, value []byte) KeyValue {
return KeyValue{Key: key, VType: BinaryType, VBlob: value}
}

// Bool returns the Boolean value stored in this KeyValue or false if it stores a different type.
// The caller must check VType before using this method.
func (kv *KeyValue) Bool() bool {
if kv.VType == BoolType {
return kv.VNum == 1
}
return false
}

// Int64 returns the Int64 value stored in this KeyValue or 0 if it stores a different type.
// The caller must check VType before using this method.
func (kv *KeyValue) Int64() int64 {
if kv.VType == Int64Type {
return kv.VNum
}
return 0
}

// Float64 returns the Float64 value stored in this KeyValue or 0 if it stores a different type.
// The caller must check VType before using this method.
func (kv *KeyValue) Float64() float64 {
if kv.VType == Float64Type {
return math.Float64frombits(uint64(kv.VNum))
}
return 0
}

// Binary returns the blob ([]byte) value stored in this KeyValue or nil if it stores a different type.
// The caller must check VType before using this method.
func (kv *KeyValue) Binary() []byte {
if kv.VType == BinaryType {
return kv.VBlob
}
return nil
}

// IsLess compares two KeyValue objects. The order is based first on the keys, then on type, and finally on the value.
func IsLess(one *KeyValue, two *KeyValue) bool {
if one.Key != two.Key {
return one.Key < two.Key
}
if one.VType != two.VType {
return one.VType < two.VType
}
switch one.VType {
case StringType:
return one.VStr < two.VStr
case BoolType, Int64Type:
return one.VNum < two.VNum
case Float64Type:
return one.Float64() < two.Float64()
case BinaryType:
l1, l2 := len(one.VBlob), len(two.VBlob)
minLen := l1
if l2 < minLen {
minLen = l2
}
for i := 0; i < minLen; i++ {
if d := int(one.VBlob[i]) - int(two.VBlob[i]); d != 0 {
return d < 0
}
}
if l1 == l2 {
return false
}
return l1 < l2
default:
return false
}
}
85 changes: 85 additions & 0 deletions model/keyvalue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package model_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/uber/jaeger/model"
)

func TestKeyValueString(t *testing.T) {
kv := model.String("x", "y")
assert.Equal(t, "x", kv.Key)
assert.Equal(t, model.StringType, kv.VType)
assert.Equal(t, "y", kv.VStr)
assert.False(t, kv.Bool())
assert.Equal(t, int64(0), kv.Int64())
assert.Equal(t, float64(0), kv.Float64())
assert.Nil(t, kv.Binary())
}

func TestKeyValueBool(t *testing.T) {
kv := model.Bool("x", true)
assert.Equal(t, "x", kv.Key)
assert.Equal(t, model.BoolType, kv.VType)
assert.True(t, kv.Bool())
}

func TestKeyValueInt64(t *testing.T) {
kv := model.Int64("x", 123)
assert.Equal(t, "x", kv.Key)
assert.Equal(t, model.Int64Type, kv.VType)
assert.Equal(t, int64(123), kv.Int64())
}

func TestKeyValueFloat64(t *testing.T) {
kv := model.Float64("x", 123.345)
assert.Equal(t, "x", kv.Key)
assert.Equal(t, model.Float64Type, kv.VType)
assert.Equal(t, 123.345, kv.Float64())
}

func TestKeyValueBinary(t *testing.T) {
kv := model.Binary("x", []byte{123, 45})
assert.Equal(t, "x", kv.Key)
assert.Equal(t, model.BinaryType, kv.VType)
assert.Equal(t, []byte{123, 45}, kv.Binary())
}

func TestKeyValueIsLess(t *testing.T) {
testCases := []struct {
name string
kv1 model.KeyValue
kv2 model.KeyValue
equal bool
}{
{name: "different keys", kv1: model.String("x", "123"), kv2: model.String("y", "123")},
{name: "same key different types", kv1: model.String("x", "123"), kv2: model.Int64("x", 123)},
{name: "different string values", kv1: model.String("x", "123"), kv2: model.String("x", "567")},
{name: "different bool values", kv1: model.Bool("x", false), kv2: model.Bool("x", true)},
{name: "different int64 values", kv1: model.Int64("x", 123), kv2: model.Int64("x", 567)},
{name: "different float64 values", kv1: model.Float64("x", 123), kv2: model.Float64("x", 567)},
{name: "different blob length", kv1: model.Binary("x", []byte{1, 2}), kv2: model.Binary("x", []byte{1, 2, 3})},
{name: "different blob values", kv1: model.Binary("x", []byte{1, 2, 3}), kv2: model.Binary("x", []byte{1, 2, 4})},
{name: "empty blob", kv1: model.Binary("x", nil), kv2: model.Binary("x", nil), equal: true},
{name: "identical blob", kv1: model.Binary("x", []byte{1, 2, 3}), kv2: model.Binary("x", []byte{1, 2, 3}), equal: true},
}
for _, tt := range testCases {
testCase := tt // capture loop var
t.Run(testCase.name, func(t *testing.T) {
if testCase.equal {
assert.False(t, model.IsLess(&testCase.kv1, &testCase.kv2))
} else {
assert.True(t, model.IsLess(&testCase.kv1, &testCase.kv2))
}
assert.False(t, model.IsLess(&testCase.kv2, &testCase.kv1))
})
}
t.Run("invalid type", func(t *testing.T) {
v1 := model.KeyValue{Key: "x", VType: model.ValueType(-1)}
v2 := model.KeyValue{Key: "x", VType: model.ValueType(-1)}
assert.False(t, model.IsLess(&v1, &v2))
assert.False(t, model.IsLess(&v2, &v1))
})
}
27 changes: 27 additions & 0 deletions model/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2016 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 model

// Log describes a micro-log entry that consists of a timestamp and one or more key-value fields
type Log struct {
Timestamp int64 `json:"timestamp"`
Fields []KeyValue `json:"fields"`
}
36 changes: 36 additions & 0 deletions model/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2016 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 model

// Process describes an instance of an application or service that emits tracing data.
type Process struct {
ServiceName string `json:"serviceName"`
Tags []Tag `json:"tags,omitempty"`
}

// NewProcess creates a new Process for given serviceName and tags.
// The tags are sorted in place and kept in the the same array/slice,
// in order to store the Process in a canonical form that can be
// useful when comparing or generating a stable hash code.
func NewProcess(serviceName string, tags []Tag) *Process {
SortTags(tags)
return &Process{ServiceName: serviceName, Tags: tags}
}
21 changes: 21 additions & 0 deletions model/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package model_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/uber/jaeger/model"
)

func TestNewProcess(t *testing.T) {
p1 := model.NewProcess("s1", []model.Tag{
model.Tag(model.String("x", "y")),
model.Tag(model.Int64("a", 1)),
})
p2 := model.NewProcess("s1", []model.Tag{
model.Tag(model.Int64("a", 1)),
model.Tag(model.String("x", "y")),
})
assert.Equal(t, p1, p2)
}
Loading

0 comments on commit c285f2d

Please sign in to comment.