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

Translate more OpenTelemetry resource conventions #4955

Merged
merged 4 commits into from
Mar 18, 2021
Merged
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
1 change: 1 addition & 0 deletions changelogs/head.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ https://github.com/elastic/apm-server/compare/7.12\...master[View commits]
* Set log and http responses for server timeout {pull}4918[4918]
* Define ES fields for cgroup.cpu and cgroup.cpuacct metrics {pull}4956[4956]
* Log gRPC tracing requests {pull}4934[4934]
* Improved coverage of translation of OpenTelemetry resource conventions {pull}4955[4955]

[float]
==== Deprecated
13 changes: 12 additions & 1 deletion model/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@ import (
)

type Container struct {
ID string
ID string
Name string
Runtime string
ImageName string
ImageTag string
}

func (c *Container) fields() common.MapStr {
var container mapStr
container.maybeSetString("name", c.Name)
container.maybeSetString("id", c.ID)
container.maybeSetString("runtime", c.Runtime)

var image mapStr
image.maybeSetString("name", c.ImageName)
image.maybeSetString("tag", c.ImageTag)
container.maybeSetMapStr("image", common.MapStr(image))
return common.MapStr(container)
}
16 changes: 16 additions & 0 deletions model/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ func TestContainerTransform(t *testing.T) {
Container: Container{ID: id},
Output: common.MapStr{"id": id},
},
{
Container: Container{Name: "container_name"},
Output: common.MapStr{"name": "container_name"},
},
{
Container: Container{Runtime: "container_runtime"},
Output: common.MapStr{"runtime": "container_runtime"},
},
{
Container: Container{ImageName: "image_name"},
Output: common.MapStr{"image": common.MapStr{"name": "image_name"}},
},
{
Container: Container{ImageTag: "image_tag"},
Output: common.MapStr{"image": common.MapStr{"tag": "image_tag"}},
},
}

for _, test := range tests {
Expand Down
83 changes: 53 additions & 30 deletions model/modeldecoder/modeldecodertest/populator.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,92 +107,115 @@ func InitStructValues(i interface{}) {
SetStructValues(i, DefaultValues())
}

// SetStructValuesOption is the type of an option which may be passed into
// SetStructValues to override the value to which a field is set. If the
// option returns false, then the field will not be updated and no more options
// will be invoked.
type SetStructValuesOption func(key string, field, value reflect.Value) bool

// SetStructValues iterates through the struct fields represented by
// the given reflect.Value and initializes all fields with the provided values
func SetStructValues(in interface{}, values *Values) {
func SetStructValues(in interface{}, values *Values, opts ...SetStructValuesOption) {
IterateStruct(in, func(f reflect.Value, key string) {
fieldVal := f
switch fKind := f.Kind(); fKind {
case reflect.String:
fieldVal = reflect.ValueOf(values.Str)
case reflect.Int:
fieldVal = reflect.ValueOf(values.Int)
case reflect.Slice:
if f.IsNil() {
f.Set(reflect.MakeSlice(f.Type(), 0, values.N))
}
var newVal reflect.Value
var elemVal reflect.Value
switch v := f.Interface().(type) {
case []string:
newVal = reflect.ValueOf(values.Str)
elemVal = reflect.ValueOf(values.Str)
case []int:
newVal = reflect.ValueOf(values.Int)
elemVal = reflect.ValueOf(values.Int)
case net.IP:
fieldVal = reflect.ValueOf(values.IP)
default:
if f.Type().Elem().Kind() != reflect.Struct {
panic(fmt.Sprintf("unhandled type %s for key %s", v, key))
}
newVal = reflect.Zero(f.Type().Elem())
elemVal = reflect.Zero(f.Type().Elem())
}
for i := 0; i < values.N; i++ {
f.Set(reflect.Append(f, newVal))
if elemVal.IsValid() {
fieldVal = reflect.MakeSlice(f.Type(), 0, values.N)
for i := 0; i < values.N; i++ {
fieldVal = reflect.Append(fieldVal, elemVal)
}
}
case reflect.Map:
if f.IsNil() {
f.Set(reflect.MakeMapWithSize(f.Type(), values.N))
}
var newVal reflect.Value
fieldVal = reflect.MakeMapWithSize(f.Type(), values.N)
var elemVal reflect.Value
switch v := f.Interface().(type) {
case map[string]interface{}, common.MapStr:
newVal = reflect.ValueOf(values.Str)
elemVal = reflect.ValueOf(values.Str)
case map[string]float64:
newVal = reflect.ValueOf(values.Float)
elemVal = reflect.ValueOf(values.Float)
default:
if f.Type().Elem().Kind() != reflect.Struct {
panic(fmt.Sprintf("unhandled type %s for key %s", v, key))
}
newVal = reflect.Zero(f.Type().Elem())
elemVal = reflect.Zero(f.Type().Elem())
}
for i := 0; i < values.N; i++ {
f.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s%v", values.Str, i)), newVal)
fieldVal.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s%v", values.Str, i)), elemVal)
}
case reflect.Struct:
var newVal interface{}
switch v := f.Interface().(type) {
case nullable.String:
v.Set(values.Str)
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.Int:
v.Set(values.Int)
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.Interface:
if strings.Contains(key, "port") {
v.Set(values.Int)
} else {
v.Set(values.Str)
}
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.Bool:
v.Set(values.Bool)
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.Float64:
v.Set(values.Float)
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.TimeMicrosUnix:
v.Set(values.Time)
newVal = v
fieldVal = reflect.ValueOf(v)
case nullable.HTTPHeader:
v.Set(values.HTTPHeader.Clone())
newVal = v
fieldVal = reflect.ValueOf(v)
default:
if f.IsZero() {
f.Set(reflect.Zero(f.Type()))
fieldVal = reflect.Zero(f.Type())
} else {
return
}
return
}
f.Set(reflect.ValueOf(newVal))
case reflect.Ptr:
if f.IsNil() {
f.Set(reflect.Zero(f.Type()))
fieldVal = reflect.Zero(f.Type())
}
return
default:
panic(fmt.Sprintf("unhandled type %s for key %s", fKind, key))
}

// Run through options, giving an opportunity to disable
// setting the field, or change the value.
setField := true
for _, opt := range opts {
if !opt(key, f, fieldVal) {
setField = false
break
}
}
if setField {
f.Set(fieldVal)
}
})
}

Expand Down
63 changes: 43 additions & 20 deletions model/modeldecoder/v2/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
package v2

import (
"net"
"reflect"
"strings"
"testing"

Expand All @@ -30,18 +30,47 @@ import (
"github.com/elastic/apm-server/model/modeldecoder/modeldecodertest"
)

// initializedMetadata returns a metadata model with
// unmappedMetadataFields holds the list of model fields that have no equivalent
// in the metadata input type.
func isUnmappedMetadataField(key string) bool {
switch key {
case
"Client.IP",
"Process.CommandLine",
"Process.Executable",
"System.Container.Runtime",
"System.Container.ImageName",
"System.Container.ImageTag",
"System.Container.Name",
"System.FullPlatform",
"System.ID",
"System.IP",
"System.OSType",
"System.Type",
"UserAgent",
"UserAgent.Name",
"UserAgent.Original":
return true
}
return false
}

func initializedMetadata() *model.Metadata {
_, metadata := initializedInputMetadata(modeldecodertest.DefaultValues())
return &metadata
}

func initializedInputMetadata(values *modeldecodertest.Values) (metadata, model.Metadata) {
var input metadata
var out model.Metadata
modeldecodertest.SetStructValues(&input, modeldecodertest.DefaultValues())
modeldecodertest.SetStructValues(&input, values)
mapToMetadataModel(&input, &out)
// initialize values that are not set by input
out.UserAgent = model.UserAgent{Name: "init", Original: "init"}
out.Client.IP = net.ParseIP("127.0.0.1")
out.System.IP = net.ParseIP("127.0.0.1")
return &out
modeldecodertest.SetStructValues(&out, values, func(key string, field, value reflect.Value) bool {
return isUnmappedMetadataField(key)
})
return input, out
}

func TestResetMetadataOnRelease(t *testing.T) {
inp := `{"metadata":{"service":{"name":"service-a"}}}`
m := fetchMetadataRoot()
Expand Down Expand Up @@ -92,16 +121,13 @@ func TestDecodeMapToMetadataModel(t *testing.T) {
// create initialized modeldecoder and empty model metadata
// map modeldecoder to model metadata and manually set
// enhanced data that are never set by the modeldecoder
var input metadata
var out model.Metadata
defaultVal := modeldecodertest.DefaultValues()
modeldecodertest.SetStructValues(&input, defaultVal)
out.System.IP, out.Client.IP = defaultVal.IP, defaultVal.IP
mapToMetadataModel(&input, &out)
input, out := initializedInputMetadata(defaultVal)

exceptions := func(key string) bool {
return strings.HasPrefix(key, "UserAgent")
return isUnmappedMetadataField(key)
}

// iterate through model and assert values are set
modeldecodertest.AssertStructValues(t, &out, exceptions, defaultVal)

Expand All @@ -125,15 +151,12 @@ func TestDecodeMapToMetadataModel(t *testing.T) {
})

t.Run("reused-memory", func(t *testing.T) {
var input metadata
var out1, out2 model.Metadata
var out2 model.Metadata
defaultVal := modeldecodertest.DefaultValues()
modeldecodertest.SetStructValues(&input, defaultVal)
mapToMetadataModel(&input, &out1)
out1.System.IP, out1.Client.IP = defaultVal.IP, defaultVal.IP //not set by decoder
input, out1 := initializedInputMetadata(defaultVal)

exceptions := func(key string) bool {
return strings.HasPrefix(key, "UserAgent")
return isUnmappedMetadataField(key)
}
// iterate through model and assert values are set
modeldecodertest.AssertStructValues(t, &out1, exceptions, defaultVal)
Expand Down
12 changes: 8 additions & 4 deletions model/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (
)

type Process struct {
Pid int
Ppid *int
Title string
Argv []string
Pid int
Ppid *int
Title string
Argv []string
CommandLine string
Executable string
}

func (p *Process) fields() common.MapStr {
Expand All @@ -40,5 +42,7 @@ func (p *Process) fields() common.MapStr {
proc.set("args", p.Argv)
}
proc.maybeSetString("title", p.Title)
proc.maybeSetString("command_line", p.CommandLine)
proc.maybeSetString("executable", p.Executable)
return common.MapStr(proc)
}
22 changes: 14 additions & 8 deletions model/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (

func TestProcessTransform(t *testing.T) {
processTitle := "node"
commandLine := "node run.js"
executablePath := "/usr/bin/node"
argv := []string{
"node",
"server.js",
Expand All @@ -44,16 +46,20 @@ func TestProcessTransform(t *testing.T) {
},
{
Process: Process{
Pid: 123,
Ppid: tests.IntPtr(456),
Title: processTitle,
Argv: argv,
Pid: 123,
Ppid: tests.IntPtr(456),
Title: processTitle,
Argv: argv,
CommandLine: commandLine,
Executable: executablePath,
},
Output: common.MapStr{
"pid": 123,
"ppid": 456,
"title": processTitle,
"args": argv,
"pid": 123,
"ppid": 456,
"title": processTitle,
"args": argv,
"command_line": commandLine,
"executable": executablePath,
},
},
}
Expand Down
17 changes: 14 additions & 3 deletions model/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ type System struct {
// TODO(axw) rename this to Name.
ConfiguredHostname string

// ID holds a unique ID for the host.
ID string

Architecture string
Platform string
FullPlatform string // Full operating system name, including version
OSType string
Type string // host type, e.g. cloud instance machine type
IP net.IP

Container Container
Expand All @@ -54,9 +60,14 @@ func (s *System) fields() common.MapStr {
system.maybeSetString("hostname", s.DetectedHostname)
system.maybeSetString("name", s.ConfiguredHostname)
system.maybeSetString("architecture", s.Architecture)
if s.Platform != "" {
system.set("os", common.MapStr{"platform": s.Platform})
}
system.maybeSetString("type", s.Type)

var os mapStr
os.maybeSetString("platform", s.Platform)
os.maybeSetString("full", s.FullPlatform)
os.maybeSetString("type", s.OSType)
system.maybeSetMapStr("os", common.MapStr(os))

if s.IP != nil {
system.set("ip", s.IP.String())
}
Expand Down
Loading