From dc56f56ddf216ed862c2e3e57b27e075acbbc415 Mon Sep 17 00:00:00 2001 From: simitt Date: Sun, 13 Jan 2019 21:47:25 +0100 Subject: [PATCH 1/8] Move user related fields to align with ECS schema. Move `context.user.email` to `user.email` Move `context.user.id` to `user.id` Move `context.user.username` to `user.name` Move `context.user.ip` to `client.ip` Move `context.user.user-agent` to `user_agent` fixes #1782 --- _meta/fields.common.yml | 93 +++++++----- ...TestPublishIntegrationErrors.approved.json | 10 +- ...blishIntegrationTransactions.approved.json | 16 ++- decoder/req_decoder.go | 1 - docs/data/elasticsearch/generated/errors.json | 10 +- .../elasticsearch/generated/transactions.json | 16 ++- docs/fields.asciidoc | 122 +++++++++------- include/fields.go | 2 +- ingest/pipeline/definition.json | 4 +- model/error/_meta/fields.yml | 6 +- model/error/event.go | 22 ++- model/error/event_test.go | 46 +++--- model/metadata/metadata.go | 5 +- model/metadata/metadata_test.go | 2 +- model/metadata/user.go | 43 ++++-- model/metadata/user_test.go | 96 ++++++++++--- model/transaction/event.go | 20 +++ model/transaction/event_test.go | 35 ++--- .../stream/package_tests/error_attrs_test.go | 5 +- .../package_tests/metadata_attrs_test.go | 6 +- .../stream/package_tests/span_attrs_test.go | 4 + .../package_tests/transaction_attrs_test.go | 6 +- processor/stream/processor_test.go | 44 ++++++ .../testIntakeIntegrationErrors.approved.json | 10 +- ...ntegrationOptionalTimestamps.approved.json | 4 +- ...stIntakeIntegrationRumErrors.approved.json | 123 ++++++++++++++++ ...keIntegrationRumTransactions.approved.json | 133 ++++++++++++++++++ ...ntakeIntegrationTransactions.approved.json | 16 ++- ...stIntegrationResultRumErrors.approved.json | 3 + ...grationResultRumTransactions.approved.json | 3 + testdata/intake-v2/transactions.ndjson | 4 +- tests/system/error.approved.json | 10 +- tests/system/test_integration.py | 9 +- tests/system/transaction.approved.json | 10 +- utility/map_str_enhancer.go | 10 ++ 35 files changed, 729 insertions(+), 220 deletions(-) create mode 100644 processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json create mode 100644 processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json create mode 100644 processor/stream/test_approved_stream_result/testIntegrationResultRumErrors.approved.json create mode 100644 processor/stream/test_approved_stream_result/testIntegrationResultRumTransactions.approved.json diff --git a/_meta/fields.common.yml b/_meta/fields.common.yml index e40976f36f6..eb18df73d62 100644 --- a/_meta/fields.common.yml +++ b/_meta/fields.common.yml @@ -47,37 +47,6 @@ description: > The status code of the http response. - - name: user - type: group - fields: - - - name: username - type: keyword - description: > - The username of the logged in user. - - - name: id - type: keyword - description: > - Identifier of the logged in user. - - - name: email - type: keyword - description: > - Email of the logged in user. - - - name: ip - type: ip - description: > - IP of the user where the event is recorded, typically a web browser. - This is obtained from the X-Forwarded-For header, of which the first entry is the IP of the original client. - This value however might not be necessarily trusted, as it can be forged by a malicious user. - - - name: user-agent - type: text - description: > - Software agent acting in behalf of a user, eg. a web browser / OS combination. - - name: request type: group fields: @@ -166,9 +135,9 @@ open_link_in_current_tab: true url_template: - min_version: "5.0.0" - value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(query_string:(analyze_wildcard:!t,query:'context.service.name:%22{{value}}%22')))" + value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(query_string:(analyze_wildcard:!t,query:'service.name:%22{{value}}%22')))" - min_version: "6.0.0-alpha1" - value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(language:lucene,query:'context.service.name:\"{{value}}\"'))" + value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(language:lucene,query:'service.name:\"{{value}}\"'))" overwrite: true - name: version @@ -355,3 +324,61 @@ type: keyword description: > Address the server is listening on. + + - name: user + type: group + fields: + + - name: name + type: keyword + description: > + The username of the logged in user. + overwrite: true + + - name: id + type: keyword + description: > + Identifier of the logged in user. + overwrite: true + + - name: email + type: keyword + description: > + Email of the logged in user. + overwrite: true + + - name: client + type: group + fields: + + - name: ip + type: ip + description: > + IP of the user where the event is recorded, typically a web browser. + This is obtained from the X-Forwarded-For header, of which the first entry is the IP of the original client. + This value however might not be necessarily trusted, as it can be forged by a malicious user. + overwrite: true + + - name: user_agent + title: User agent + description: > + The user_agent fields normally come from a browser request. They often + show up in web service logs coming from the parsed user agent string. + type: group + overwrite: true + fields: + + - name: original + type: keyword + description: > + Unparsed version of the user_agent. + example: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1" + overwrite: true + + multi_fields: + - name: text + type: text + description: > + Software agent acting in behalf of a user, eg. a web browser / OS combination. + overwrite: true + diff --git a/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json b/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json index 0ef7dca8a91..ef6e7594d85 100644 --- a/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json +++ b/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json @@ -70,11 +70,6 @@ }, "tags": { "organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8" - }, - "user": { - "email": "foo@example.com", - "id": 99, - "username": "foo" } }, "error": { @@ -267,6 +262,11 @@ }, "timestamp": { "us": 1494342245999999 + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { diff --git a/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json b/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json index 21a6350f40a..487111bb67a 100644 --- a/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json +++ b/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json @@ -75,6 +75,9 @@ "started": 43 }, "type": "request" + }, + "user": { + "id": "123user" } }, { @@ -157,11 +160,6 @@ "tag2": 12, "tag3": 12.45, "tag4": false - }, - "user": { - "email": "foo@example.com", - "id": "99", - "username": "foo" } }, "host": { @@ -227,6 +225,11 @@ "started": 17 }, "type": "request" + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { @@ -319,6 +322,9 @@ "started": 436 }, "type": "request" + }, + "user": { + "id": "123user" } } ] diff --git a/decoder/req_decoder.go b/decoder/req_decoder.go index 2aff42c2beb..637286b863e 100644 --- a/decoder/req_decoder.go +++ b/decoder/req_decoder.go @@ -151,7 +151,6 @@ func DecodeSourcemapFormData(req *http.Request) (map[string]interface{}, error) if err != nil { return nil, err } - payload := map[string]interface{}{ "sourcemap": string(sourcemapBytes), "service_name": req.FormValue("service_name"), diff --git a/docs/data/elasticsearch/generated/errors.json b/docs/data/elasticsearch/generated/errors.json index 794647ced75..ec94dcd8d5b 100644 --- a/docs/data/elasticsearch/generated/errors.json +++ b/docs/data/elasticsearch/generated/errors.json @@ -65,11 +65,6 @@ }, "tags": { "organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8" - }, - "user": { - "email": "foo@example.com", - "id": 99, - "username": "foo" } }, "error": { @@ -255,6 +250,11 @@ }, "timestamp": { "us": 1494342245999999 + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { diff --git a/docs/data/elasticsearch/generated/transactions.json b/docs/data/elasticsearch/generated/transactions.json index 7f03829b29c..4b15cf17236 100644 --- a/docs/data/elasticsearch/generated/transactions.json +++ b/docs/data/elasticsearch/generated/transactions.json @@ -63,6 +63,9 @@ "started": 43 }, "type": "request" + }, + "user": { + "id": "123user" } }, { @@ -140,11 +143,6 @@ "tag2": 12, "tag3": 12.45, "tag4": false - }, - "user": { - "email": "foo@example.com", - "id": "99", - "username": "foo" } }, "host": { @@ -203,6 +201,11 @@ "started": 17 }, "type": "request" + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { @@ -283,6 +286,9 @@ "started": 436 }, "type": "request" + }, + "user": { + "id": "123user" } } ] diff --git a/docs/fields.asciidoc b/docs/fields.asciidoc index d56a8e9977b..5db218ba6f7 100644 --- a/docs/fields.asciidoc +++ b/docs/fields.asciidoc @@ -91,57 +91,6 @@ The status code of the http response. -- -*`context.user.username`*:: -+ --- -type: keyword - -The username of the logged in user. - - --- - -*`context.user.id`*:: -+ --- -type: keyword - -Identifier of the logged in user. - - --- - -*`context.user.email`*:: -+ --- -type: keyword - -Email of the logged in user. - - --- - -*`context.user.ip`*:: -+ --- -type: ip - -IP of the user where the event is recorded, typically a web browser. This is obtained from the X-Forwarded-For header, of which the first entry is the IP of the original client. This value however might not be necessarily trusted, as it can be forged by a malicious user. - - --- - -*`context.user.user-agent`*:: -+ --- -type: text - -Software agent acting in behalf of a user, eg. a web browser / OS combination. - - --- - - [float] == url fields @@ -549,6 +498,77 @@ type: keyword Address the server is listening on. +-- + + +*`user.name`*:: ++ +-- +type: keyword + +The username of the logged in user. + + +-- + +*`user.id`*:: ++ +-- +type: keyword + +Identifier of the logged in user. + + +-- + +*`user.email`*:: ++ +-- +type: keyword + +Email of the logged in user. + + +-- + + +*`client.ip`*:: ++ +-- +type: ip + +IP of the user where the event is recorded, typically a web browser. This is obtained from the X-Forwarded-For header, of which the first entry is the IP of the original client. This value however might not be necessarily trusted, as it can be forged by a malicious user. + + +-- + +[float] +== user_agent fields + +The user_agent fields normally come from a browser request. They often show up in web service logs coming from the parsed user agent string. + + + +*`user_agent.original`*:: ++ +-- +type: keyword + +example: Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1 + +Unparsed version of the user_agent. + + +*`user_agent.original.text`*:: ++ +-- +type: text + +Software agent acting in behalf of a user, eg. a web browser / OS combination. + + +-- + -- [[exported-fields-apm-error]] diff --git a/include/fields.go b/include/fields.go index d8e503dc82f..d3fb859bf91 100644 --- a/include/fields.go +++ b/include/fields.go @@ -31,5 +31,5 @@ func init() { // Asset returns asset data func Asset() string { - return "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" + return "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" } diff --git a/ingest/pipeline/definition.json b/ingest/pipeline/definition.json index 87963fddfb7..48940d209aa 100644 --- a/ingest/pipeline/definition.json +++ b/ingest/pipeline/definition.json @@ -12,8 +12,8 @@ }, { "user_agent" : { - "field": "context.user.user-agent", - "target_field": "context.user.user_agent", + "field": "user_agent.original", + "target_field": "user_agent_parsed", "ignore_missing": true } } diff --git a/model/error/_meta/fields.yml b/model/error/_meta/fields.yml index b5e45a86061..e347cc66987 100644 --- a/model/error/_meta/fields.yml +++ b/model/error/_meta/fields.yml @@ -4,16 +4,16 @@ fields: - name: view errors type: keyword - script: "doc['service.name'].size() > 0 ? doc['service.name'].value : doc['context.service.name'].size() > 0 ? doc['context.service.name'].value : null" + script: "doc['service.name'].size() > 0 ? doc['service.name'].value : doc['service.name'].size() > 0 ? doc['service.name'].value : null" language: painless format: url label_template: "View Errors" open_link_in_current_tab: true url_template: - min_version: 5.0.0 - value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(query_string:(analyze_wildcard:!t,query:'context.service.name:%22{{value}}%22')))" + value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(query_string:(analyze_wildcard:!t,query:'service.name:%22{{value}}%22')))" - min_version: 6.0.0-alpha1 - value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(language:lucene,query:'context.service.name:\"{{value}}\"'))" + value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(language:lucene,query:'service.name:\"{{value}}\"'))" - name: error id icon type: keyword diff --git a/model/error/event.go b/model/error/event.go index 2bd59552193..274168f8a77 100644 --- a/model/error/event.go +++ b/model/error/event.go @@ -28,6 +28,8 @@ import ( "strconv" "time" + "github.com/elastic/apm-server/model/metadata" + "github.com/santhosh-tekuri/jsonschema" m "github.com/elastic/apm-server/model" @@ -74,6 +76,8 @@ type Event struct { TransactionSampled *bool + User *metadata.User + data common.MapStr } @@ -161,6 +165,14 @@ func DecodeEvent(input interface{}, err error) (transform.Transformable, error) return nil, err } + if ok, _ := e.Context.HasKey("user"); ok { + user, err := e.Context.GetValue("user") + e.User, err = metadata.DecodeUser(user, err) + if err != nil { + return nil, err + } + } + return &e, nil } @@ -178,8 +190,12 @@ func (e *Event) Transform(tctx *transform.Context) []beat.Event { "error": e.fields(tctx), "processor": processorEntry, } - tctx.Metadata.Merge(fields) + delete(e.Context, "user") utility.Add(fields, "context", e.Context) + utility.Add(fields, "user", e.User.Fields()) + utility.Add(fields, "client", e.User.ClientFields()) + utility.Add(fields, "user_agent", e.User.UserAgentFields()) + tctx.Metadata.Merge(fields) if e.TransactionSampled != nil || (e.TransactionId != nil && *e.TransactionId != "") { transaction := common.MapStr{} @@ -208,6 +224,10 @@ func (e *Event) fields(tctx *transform.Context) common.MapStr { e.data = common.MapStr{} e.add("id", e.Id) + if tctx.Metadata.User != nil { + e.add("user_agent.original", tctx.Metadata.User.UserAgent) + } + e.addException(tctx) e.addLog(tctx) diff --git a/model/error/event_test.go b/model/error/event_test.go index f7cc9958744..121fadeeb82 100644 --- a/model/error/event_test.go +++ b/model/error/event_test.go @@ -81,7 +81,9 @@ func TestErrorEventDecode(t *testing.T) { id, culprit := "123", "foo()" parentId, traceId, transactionId := "0123456789abcdef", "01234567890123456789abcdefabcdef", "abcdefabcdef0000" - context := map[string]interface{}{"a": "b"} + name, userId, email, userIp := "jane", "abc123", "j@d.com", "127.0.0.1" + context := map[string]interface{}{"a": "b", "user": map[string]interface{}{ + "username": name, "email": email, "ip": userIp, "id": userId}} code, module, attrs, exType, handled := "200", "a", "attr", "errorEx", false exMsg, paramMsg, level, logger := "Exception Msg", "log pm", "error", "mylogger" sampled := true @@ -112,6 +114,7 @@ func TestErrorEventDecode(t *testing.T) { Culprit: &culprit, Context: context, Timestamp: timestampParsed, + User: &metadata.User{Id: &userId, Name: &name, IP: &userIp, Email: &email}, }, }, { @@ -221,7 +224,6 @@ func TestErrorEventDecode(t *testing.T) { } { transformable, err := DecodeEvent(test.input, test.inpErr) - fmt.Println(err) if test.e != nil { event := transformable.(*Event) assert.Equal(t, test.e, event, fmt.Sprintf("Failed at idx %v", idx)) @@ -403,6 +405,9 @@ func TestEvents(t *testing.T) { trId := "945254c5-67a5-417e-8a4e-aa29efcbfb79" sampledTrue, sampledFalse := true, false + email, userIp, userAgent := "m@m.com", "127.0.0.1", "js-1.0" + uid := "1234567889" + tests := []struct { Transformable transform.Transformable Output common.MapStr @@ -411,15 +416,12 @@ func TestEvents(t *testing.T) { { Transformable: &Event{Timestamp: timestamp}, Output: common.MapStr{ - "context": common.MapStr{ - "service": common.MapStr{ - "agent": common.MapStr{"name": "", "version": ""}, - "name": "myservice", - }, - }, + "agent": common.MapStr{"name": "", "version": ""}, + "service": common.MapStr{"name": "myservice"}, "error": common.MapStr{ "grouping_key": "d41d8cd98f00b204e9800998ecf8427e", }, + "user": common.MapStr{"id": uid}, "processor": common.MapStr{"event": "error", "name": "error"}, "timestamp": common.MapStr{"us": timestampUs}, }, @@ -429,15 +431,12 @@ func TestEvents(t *testing.T) { Transformable: &Event{Timestamp: timestamp, TransactionSampled: &sampledFalse}, Output: common.MapStr{ "transaction": common.MapStr{"sampled": false}, - "context": common.MapStr{ - "service": common.MapStr{ - "agent": common.MapStr{"name": "", "version": ""}, - "name": "myservice", - }, - }, + "agent": common.MapStr{"name": "", "version": ""}, + "service": common.MapStr{"name": "myservice"}, "error": common.MapStr{ "grouping_key": "d41d8cd98f00b204e9800998ecf8427e", }, + "user": common.MapStr{"id": uid}, "processor": common.MapStr{"event": "error", "name": "error"}, "timestamp": common.MapStr{"us": timestampUs}, }, @@ -446,7 +445,7 @@ func TestEvents(t *testing.T) { { Transformable: &Event{ Timestamp: timestamp, - Context: common.MapStr{"foo": "bar", "user": common.MapStr{"email": "m@m.com"}}, + Context: common.MapStr{"foo": "bar", "user": common.MapStr{"email": "test@m.com"}}, Log: baseLog(), Exception: &Exception{ Message: &exMsg, @@ -454,16 +453,18 @@ func TestEvents(t *testing.T) { }, TransactionId: &trId, TransactionSampled: &sampledTrue, + User: &metadata.User{Email: &email, IP: &userIp, UserAgent: &userAgent}, }, Output: common.MapStr{ "context": common.MapStr{ - "foo": "bar", "user": common.MapStr{"email": "m@m.com"}, - "service": common.MapStr{ - "name": "myservice", - "agent": common.MapStr{"name": "", "version": ""}, - }, + "foo": "bar", }, + "service": common.MapStr{"name": "myservice"}, + "agent": common.MapStr{"name": "", "version": ""}, + "user": common.MapStr{"email": email}, + "client": common.MapStr{"ip": userIp}, + "user_agent": common.MapStr{"original": userAgent}, "error": common.MapStr{ "grouping_key": "1d1e44ffdf01cad5117a72fd42e4fdf4", "log": common.MapStr{"message": "error log message"}, @@ -488,9 +489,7 @@ func TestEvents(t *testing.T) { }, } - me := metadata.NewMetadata( - &service, nil, nil, nil, - ) + me := metadata.NewMetadata(&service, nil, nil, &metadata.User{Id: &uid}) tctx := &transform.Context{ Metadata: *me, Config: transform.Config{SmapMapper: &sourcemap.SmapMapper{}}, @@ -501,6 +500,7 @@ func TestEvents(t *testing.T) { outputEvents := test.Transformable.Transform(tctx) require.Len(t, outputEvents, 1) outputEvent := outputEvents[0] + assert.Equal(t, test.Output, outputEvent.Fields, fmt.Sprintf("Failed at idx %v; %s", idx, test.Msg)) assert.Equal(t, test.Output["timestamp"], outputEvent.Fields["timestamp"], fmt.Sprintf("Failed at idx %v; %s", idx, test.Msg)) assert.Equal(t, timestamp, outputEvent.Timestamp, fmt.Sprintf("Bad timestamp at idx %v; %s", idx, test.Msg)) } diff --git a/model/metadata/metadata.go b/model/metadata/metadata.go index be87ee75fac..8a0bbbcc02d 100644 --- a/model/metadata/metadata.go +++ b/model/metadata/metadata.go @@ -80,7 +80,10 @@ func (m *Metadata) Merge(fields common.MapStr) common.MapStr { utility.Add(fields, "host", m.System.fields()) utility.Add(fields, "process", m.Process.fields()) utility.MergeAdd(fields, "service", m.Service.fields()) - utility.MergeAdd(fields, "user", m.User.fields()) + utility.AddIfNil(fields, "user", m.User.Fields()) + utility.AddIfNil(fields, "client", m.User.ClientFields()) + utility.AddIfNil(fields, "user_agent", m.User.UserAgentFields()) + return fields } diff --git a/model/metadata/metadata_test.go b/model/metadata/metadata_test.go index 8260ec560f7..8fc5fbafb13 100644 --- a/model/metadata/metadata_test.go +++ b/model/metadata/metadata_test.go @@ -170,7 +170,7 @@ func TestMetadataMerge(t *testing.T) { "name": "myservice", }, "process": common.MapStr{"pid": pid}, - "user": common.MapStr{"id": "12321", "email": "override@email.com"}, + "user": common.MapStr{"email": "override@email.com"}, }, }, } { diff --git a/model/metadata/user.go b/model/metadata/user.go index 79ccf1181a6..3035dd0045f 100644 --- a/model/metadata/user.go +++ b/model/metadata/user.go @@ -18,7 +18,9 @@ package metadata import ( + "encoding/json" "errors" + "fmt" "github.com/elastic/apm-server/utility" "github.com/elastic/beats/libbeat/common" @@ -27,7 +29,7 @@ import ( type User struct { Id *string Email *string - Username *string + Name *string IP *string UserAgent *string } @@ -42,24 +44,49 @@ func DecodeUser(input interface{}, err error) (*User, error) { } decoder := utility.ManualDecoder{} user := User{ - Id: decoder.StringPtr(raw, "id"), - Email: decoder.StringPtr(raw, "email"), - Username: decoder.StringPtr(raw, "username"), - IP: decoder.StringPtr(raw, "ip"), UserAgent: decoder.StringPtr(raw, "user-agent"), + IP: decoder.StringPtr(raw, "ip"), + Name: decoder.StringPtr(raw, "username"), + Email: decoder.StringPtr(raw, "email"), + } + + //id can be string or int + tmp := decoder.Interface(raw, "id") + fmt.Println(tmp) + if tmp != nil { + if t, ok := tmp.(json.Number); ok { + id := t.String() + user.Id = &id + } else if t, ok := tmp.(string); ok && t != "" { + user.Id = &t + } } return &user, decoder.Err } -func (u *User) fields() common.MapStr { +func (u *User) Fields() common.MapStr { if u == nil { return nil } user := common.MapStr{} utility.Add(user, "id", u.Id) utility.Add(user, "email", u.Email) - utility.Add(user, "username", u.Username) + utility.Add(user, "name", u.Name) + return user +} + +func (u *User) ClientFields() common.MapStr { + if u == nil { + return nil + } + user := common.MapStr{} utility.Add(user, "ip", u.IP) - utility.Add(user, "user-agent", u.UserAgent) return user } + +func (u *User) UserAgentFields() common.MapStr { + if u == nil || u.UserAgent == nil { + return nil + } + return common.MapStr{"original": *u.UserAgent} +} diff --git a/model/metadata/user_test.go b/model/metadata/user_test.go index 412483eed9b..9c55dc0378c 100644 --- a/model/metadata/user_test.go +++ b/model/metadata/user_test.go @@ -18,6 +18,7 @@ package metadata import ( + "encoding/json" "errors" "testing" @@ -26,11 +27,11 @@ import ( "github.com/elastic/beats/libbeat/common" ) -func TestUserTransform(t *testing.T) { +func TestUserFields(t *testing.T) { id := "1234" ip := "127.0.0.1" email := "test@mail.co" - username := "user123" + name := "user123" userAgent := "rum-1.0" tests := []struct { @@ -46,21 +47,87 @@ func TestUserTransform(t *testing.T) { Id: &id, IP: &ip, Email: &email, - Username: &username, + Name: &name, UserAgent: &userAgent, }, Output: common.MapStr{ - "ip": "127.0.0.1", - "id": "1234", - "email": "test@mail.co", - "username": "user123", - "user-agent": "rum-1.0", + "id": "1234", + "email": "test@mail.co", + "name": "user123", }, }, } for _, test := range tests { - output := test.User.fields() + output := test.User.Fields() + assert.Equal(t, test.Output, output) + } +} + +func TestUserClientFields(t *testing.T) { + id := "1234" + ip := "127.0.0.1" + email := "test@mail.co" + name := "user123" + userAgent := "rum-1.0" + + tests := []struct { + User User + Output common.MapStr + }{ + { + User: User{}, + Output: common.MapStr{}, + }, + { + User: User{ + Id: &id, + IP: &ip, + Email: &email, + Name: &name, + UserAgent: &userAgent, + }, + Output: common.MapStr{ + "ip": "127.0.0.1", + }, + }, + } + + for _, test := range tests { + output := test.User.ClientFields() + assert.Equal(t, test.Output, output) + } +} + +func TestUserAgentFields(t *testing.T) { + id := "1234" + ip := "127.0.0.1" + email := "test@mail.co" + name := "user123" + userAgent := "rum-1.0" + + tests := []struct { + User User + Output common.MapStr + }{ + { + User: User{}, + Output: nil, + }, + { + User: User{ + Id: &id, + IP: &ip, + Email: &email, + Name: &name, + UserAgent: &userAgent, + }, + Output: common.MapStr{"original": "rum-1.0"}, + }, + } + + for _, test := range tests { + output := test.User.UserAgentFields() assert.Equal(t, test.Output, output) } } @@ -77,19 +144,14 @@ func TestUserDecode(t *testing.T) { {input: nil, inputErr: nil, err: nil, u: nil}, {input: nil, inputErr: inpErr, err: inpErr, u: nil}, {input: "", err: errors.New("Invalid type for user"), u: nil}, - { - input: map[string]interface{}{"id": 1}, - err: errors.New("Error fetching field"), - u: &User{Id: nil, Email: nil, Username: nil, IP: nil, UserAgent: nil}, - }, + {input: map[string]interface{}{"id": json.Number("12")}, inputErr: nil, err: nil, u: &User{Id: &id}}, { input: map[string]interface{}{ - "id": id, "email": mail, "username": name, - "ip": ip, "user-agent": agent, + "id": id, "email": mail, "username": name, "ip": ip, "user-agent": agent, }, err: nil, u: &User{ - Id: &id, Email: &mail, Username: &name, IP: &ip, UserAgent: &agent, + Id: &id, Email: &mail, Name: &name, IP: &ip, UserAgent: &agent, }, }, } { diff --git a/model/transaction/event.go b/model/transaction/event.go index afb6037f296..dcb3e311cfe 100644 --- a/model/transaction/event.go +++ b/model/transaction/event.go @@ -21,6 +21,8 @@ import ( "errors" "time" + "github.com/elastic/apm-server/model/metadata" + "github.com/santhosh-tekuri/jsonschema" "github.com/elastic/apm-server/model/transaction/generated/schema" @@ -62,6 +64,7 @@ type Event struct { Marks common.MapStr Sampled *bool SpanCount SpanCount + User *metadata.User ParentId *string TraceId string @@ -106,6 +109,14 @@ func DecodeEvent(input interface{}, err error) (transform.Transformable, error) return nil, decoder.Err } + if ok, _ := e.Context.HasKey("user"); ok { + user, err := e.Context.GetValue("user") + e.User, err = metadata.DecodeUser(user, err) + if err != nil { + return nil, err + } + } + return &e, nil } @@ -117,6 +128,10 @@ func (t *Event) fields(tctx *transform.Context) common.MapStr { utility.Add(tx, "result", t.Result) utility.Add(tx, "marks", t.Marks) + if tctx.Metadata.User != nil { + utility.Add(tx, "user_agent.original", tctx.Metadata.User.UserAgent) + } + if t.Sampled == nil { utility.Add(tx, "sampled", true) } else { @@ -134,6 +149,7 @@ func (t *Event) fields(tctx *transform.Context) common.MapStr { } utility.Add(tx, "span_count", spanCount) } + return tx } @@ -149,10 +165,14 @@ func (e *Event) Transform(tctx *transform.Context) []beat.Event { "processor": processorEntry, transactionDocType: e.fields(tctx), } + delete(e.Context, "user") utility.Add(fields, "context", e.Context) utility.AddId(fields, "parent", e.ParentId) utility.AddId(fields, "trace", &e.TraceId) utility.Add(fields, "timestamp", utility.TimeAsMicros(e.Timestamp)) + utility.Add(fields, "user", e.User.Fields()) + utility.Add(fields, "client", e.User.ClientFields()) + utility.Add(fields, "user_agent", e.User.UserAgentFields()) tctx.Metadata.Merge(fields) events = append(events, beat.Event{Fields: fields, Timestamp: e.Timestamp}) diff --git a/model/transaction/event_test.go b/model/transaction/event_test.go index 796e55b04db..93a94c22833 100644 --- a/model/transaction/event_test.go +++ b/model/transaction/event_test.go @@ -61,7 +61,9 @@ func TestTransactionEventDecode(t *testing.T) { timestampEpoch := json.Number(fmt.Sprintf("%d", timestampParsed.UnixNano()/1000)) traceId, parentId := "0147258369012345abcdef0123456789", "abcdef0123456789" dropped, started, duration := 12, 6, 1.67 - context := map[string]interface{}{"a": "b"} + name, userId, email, userIp := "jane", "abc123", "j@d.com", "127.0.0.1" + context := map[string]interface{}{"a": "b", "user": map[string]interface{}{ + "username": name, "email": email, "ip": userIp, "id": userId}} marks := map[string]interface{}{"k": "b"} sampled := true @@ -70,24 +72,6 @@ func TestTransactionEventDecode(t *testing.T) { err error e *Event }{ - // traceId missing - { - input: map[string]interface{}{ - "id": id, "type": trType, "duration": duration, "timestamp": timestampEpoch, - "span_count": map[string]interface{}{"started": 6.0}}, - err: errors.New("Error fetching field"), - }, - // minimal event - { - input: map[string]interface{}{ - "id": id, "type": trType, "duration": duration, "timestamp": timestampEpoch, - "trace_id": traceId, "span_count": map[string]interface{}{"started": 6.0}}, - e: &Event{ - Id: id, Type: trType, TraceId: traceId, - Duration: duration, Timestamp: timestampParsed, - SpanCount: SpanCount{Started: &started}, - }, - }, // full event, ignoring spans { input: map[string]interface{}{ @@ -105,6 +89,7 @@ func TestTransactionEventDecode(t *testing.T) { Duration: duration, Timestamp: timestampParsed, Context: context, Marks: marks, Sampled: &sampled, SpanCount: SpanCount{Dropped: &dropped, Started: &started}, + User: &metadata.User{Id: &userId, Name: &name, IP: &userIp, Email: &email}, }, }, } { @@ -194,7 +179,7 @@ func TestEventTransform(t *testing.T) { Result: &result, Timestamp: time.Now(), Duration: 65.98, - Context: common.MapStr{"foo": "bar"}, + Context: map[string]interface{}{"foo": "bar"}, Sampled: &sampled, SpanCount: SpanCount{Started: &startedSpans, Dropped: &dropped}, }, @@ -225,6 +210,8 @@ func TestEventsTransformWithMetadata(t *testing.T) { platform := "x64" timestamp, _ := time.Parse(time.RFC3339, "2019-01-03T15:17:04.908596+01:00") timestampUs := timestamp.UnixNano() / 1000 + id, name, ip, userAgent := "123", "jane", "63.23.123.4", "node-js-2.3" + user := metadata.User{Id: &id, Name: &name, IP: &ip, UserAgent: &userAgent} service := metadata.Service{Name: "myservice"} system := &metadata.System{ @@ -276,13 +263,15 @@ func TestEventsTransformWithMetadata(t *testing.T) { "sampled": true, }, } - txWithContext := Event{Timestamp: timestamp, Context: common.MapStr{"foo": "bar", "user": common.MapStr{"id": "55"}}} + txWithContext := Event{Timestamp: timestamp, Context: common.MapStr{"foo": "bar"}, User: &user} txWithContextEs := common.MapStr{ "agent": common.MapStr{"name": "", "version": ""}, "context": common.MapStr{ - "foo": "bar", - "user": common.MapStr{"id": "55"}, + "foo": "bar", }, + "user": common.MapStr{"id": "123", "name": "jane"}, + "client": common.MapStr{"ip": "63.23.123.4"}, + "user_agent": common.MapStr{"original": userAgent}, "host": common.MapStr{ "architecture": "darwin", "hostname": "a.b.c", diff --git a/processor/stream/package_tests/error_attrs_test.go b/processor/stream/package_tests/error_attrs_test.go index 75e2a4c3407..8840548d07a 100644 --- a/processor/stream/package_tests/error_attrs_test.go +++ b/processor/stream/package_tests/error_attrs_test.go @@ -44,16 +44,18 @@ func errorPayloadAttrsNotInFields() *tests.Set { tests.Group("error.exception.attributes"), "error.exception.stacktrace", "error.log.stacktrace", + tests.Group("context.user"), ) } func errorFieldsNotInPayloadAttrs() *tests.Set { return tests.NewSet( "view errors", "error id icon", - "context.user.user-agent", "context.user.ip", "context.http", "context.http.status_code", "host.ip", tests.Group("observer"), + tests.Group("user"), + tests.Group("client"), ) } @@ -120,6 +122,7 @@ func errorKeywordExceptionKeys() *tests.Set { tests.Group("observer"), tests.Group("process"), tests.Group("service"), + tests.Group("user"), ) } diff --git a/processor/stream/package_tests/metadata_attrs_test.go b/processor/stream/package_tests/metadata_attrs_test.go index a8c56df5bd6..486d94f4de2 100644 --- a/processor/stream/package_tests/metadata_attrs_test.go +++ b/processor/stream/package_tests/metadata_attrs_test.go @@ -104,8 +104,7 @@ func TestMetadataPayloadAttrsMatchFields(t *testing.T) { {Template: "system.platform", Mapping: "host.os.platform"}, {Template: "system", Mapping: "host"}, {Template: "service.agent", Mapping: "agent"}, - {Template: "user.username", Mapping: "context.user.username"}, - {Template: "user", Mapping: "context.user"}, + {Template: "user.username", Mapping: "user.name"}, {Template: "process.argv", Mapping: "process.args"}, } setup.EventFieldsMappedToTemplateFields(t, eventFields, mappingFields) @@ -129,12 +128,13 @@ func TestKeywordLimitationOnMetadataAttrs(t *testing.T) { tests.Group("transaction"), tests.Group("parent"), tests.Group("trace"), + "user_agent.original", ), []tests.FieldTemplateMapping{ {Template: "agent", Mapping: "service.agent"}, {Template: "host.os.platform", Mapping: "system.platform"}, {Template: "host", Mapping: "system"}, - {Template: "context.user", Mapping: "user"}, + {Template: "user.name", Mapping: "user.username"}, }, ) } diff --git a/processor/stream/package_tests/span_attrs_test.go b/processor/stream/package_tests/span_attrs_test.go index 89476e6dc36..8298b30bc47 100644 --- a/processor/stream/package_tests/span_attrs_test.go +++ b/processor/stream/package_tests/span_attrs_test.go @@ -46,6 +46,7 @@ func spanPayloadAttrsNotInFields() *tests.Set { "context.http", "context.http.url", "context.http.method", + tests.Group("context.user"), ) } @@ -58,6 +59,8 @@ func spanFieldsNotInPayloadAttrs() *tests.Set { tests.Group("observer"), tests.Group("process"), tests.Group("service"), + tests.Group("user"), + tests.Group("client"), ), // not valid for the span context transactionContext(), @@ -121,6 +124,7 @@ func spanKeywordExceptionKeys() *tests.Set { tests.Group("host"), tests.Group("process"), tests.Group("service"), + tests.Group("user"), ), transactionContext(), ) diff --git a/processor/stream/package_tests/transaction_attrs_test.go b/processor/stream/package_tests/transaction_attrs_test.go index 4b3ac88de5b..49289e100d4 100644 --- a/processor/stream/package_tests/transaction_attrs_test.go +++ b/processor/stream/package_tests/transaction_attrs_test.go @@ -43,18 +43,19 @@ func transactionPayloadAttrsNotInFields() *tests.Set { return tests.NewSet( tests.Group("transaction.marks."), "transaction.span_count.started", + tests.Group("context.user"), ) } func transactionFieldsNotInPayloadAttrs() *tests.Set { return tests.NewSet( - "context.user.user-agent", - "context.user.ip", "context.http", "context.http.status_code", "host.ip", "transaction.marks.*.*", tests.Group("observer"), + tests.Group("user"), + tests.Group("client"), ) } @@ -97,6 +98,7 @@ func transactionKeywordExceptionKeys() *tests.Set { tests.Group("host"), tests.Group("process"), tests.Group("service"), + tests.Group("user"), ) } diff --git a/processor/stream/processor_test.go b/processor/stream/processor_test.go index eec2f1a7d06..77f458dd503 100644 --- a/processor/stream/processor_test.go +++ b/processor/stream/processor_test.go @@ -146,6 +146,50 @@ func TestIntegration(t *testing.T) { } } +func TestIntegrationRum(t *testing.T) { + report := func(ctx context.Context, p publish.PendingReq) error { + var events []beat.Event + for _, transformable := range p.Transformables { + events = append(events, transformable.Transform(p.Tcontext)...) + } + name := ctx.Value("name").(string) + verifyErr := tests.ApproveEvents(events, name, nil) + if verifyErr != nil { + assert.Fail(t, fmt.Sprintf("Test %s failed with error: %s", name, verifyErr.Error())) + } + return nil + } + + for _, test := range []struct { + path string + name string + }{ + {path: "errors_rum.ndjson", name: "RumErrors"}, + {path: "transactions_spans_rum.ndjson", name: "RumTransactions"}, + } { + t.Run(test.name, func(t *testing.T) { + b, err := loader.LoadDataAsBytes(filepath.Join("../testdata/intake-v2/", test.path)) + require.NoError(t, err) + bodyReader := bytes.NewBuffer(b) + + name := fmt.Sprintf("test_approved_es_documents/testIntakeIntegration%s", test.name) + ctx := context.WithValue(context.Background(), "name", name) + reqTimestamp, err := time.Parse(time.RFC3339, "2018-08-01T10:00:00Z") + ctx = utility.ContextWithRequestTime(ctx, reqTimestamp) + + reqDecoderMeta := map[string]interface{}{ + "user": map[string]interface{}{ + "user-agent": "rum-2.0", + "ip": "192.0.0.1", + }, + } + + actualResult := (&Processor{MaxEventSize: 100 * 1024}).HandleStream(ctx, nil, reqDecoderMeta, bodyReader, report) + assertApproveResult(t, actualResult, test.name) + }) + } +} + func TestRateLimiting(t *testing.T) { report := func(ctx context.Context, p publish.PendingReq) error { return nil diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json index 794647ced75..ec94dcd8d5b 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json @@ -65,11 +65,6 @@ }, "tags": { "organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8" - }, - "user": { - "email": "foo@example.com", - "id": 99, - "username": "foo" } }, "error": { @@ -255,6 +250,11 @@ }, "timestamp": { "us": 1494342245999999 + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationOptionalTimestamps.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationOptionalTimestamps.approved.json index f03b9ab875d..86849416664 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationOptionalTimestamps.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationOptionalTimestamps.approved.json @@ -65,7 +65,7 @@ "user": { "email": "s@test.com", "id": "123", - "username": "john" + "name": "john" } }, { @@ -153,7 +153,7 @@ "user": { "email": "s@test.com", "id": "123", - "username": "john" + "name": "john" } } ] diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json new file mode 100644 index 00000000000..42cb7269e6b --- /dev/null +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json @@ -0,0 +1,123 @@ +{ + "events": [ + { + "@timestamp": "2017-12-08T12:18:50.291Z", + "agent": { + "name": "apm-js", + "version": "0.0.0" + }, + "client": { + "ip": "192.0.0.1" + }, + "context": { + "environment": { + "browserHeight": 726, + "browserWidth": 150, + "language": "en-US", + "platform": "MacIntel", + "screenHeight": 800, + "screenWidth": 1280, + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36" + }, + "page": { + "host": "localhost", + "location": "http://localhost:8000/test/e2e/general-usecase/", + "referer": "http://localhost:8000/test/e2e/" + } + }, + "error": { + "culprit": "test/e2e/general-usecase/bundle.js.map", + "exception": { + "message": "Uncaught Error: timeout test error", + "stacktrace": [ + { + "abs_path": "http://localhost:8000/test/../test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "library_frame": true, + "line": { + "column": 18, + "number": 1 + } + }, + { + "abs_path": "http://localhost:8000/test/./e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "invokeTask", + "library_frame": false, + "line": { + "column": 181, + "number": 1 + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "runTask", + "line": { + "column": 15, + "number": 1 + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "invoke", + "line": { + "column": 199, + "number": 1 + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "timer", + "line": { + "column": 33, + "number": 1 + } + } + ], + "type": "Error" + }, + "grouping_key": "52fbc9c2d1a61bf905b4a11c708006fd", + "id": "aba2688e033848ce9c4e4005f1caa534", + "log": { + "message": "Uncaught Error: log timeout test error", + "stacktrace": [ + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "line": { + "column": 18, + "number": 1 + } + } + ] + }, + "user_agent.original": "rum-2.0" + }, + "processor": { + "event": "error", + "name": "error" + }, + "service": { + "name": "apm-agent-js", + "version": "1.0.1" + }, + "timestamp": { + "us": 1512735530291000 + }, + "user_agent": { + "original": "rum-2.0" + } + } + ] +} diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json new file mode 100644 index 00000000000..61bb52b1eb8 --- /dev/null +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json @@ -0,0 +1,133 @@ +{ + "events": [ + { + "@timestamp": "2018-08-01T10:00:00Z", + "agent": { + "name": "apm-js", + "version": "0.0.0" + }, + "client": { + "ip": "192.0.0.1" + }, + "context": { + "_metrics": { + "connectEnd": 14, + "connectStart": 14, + "domComplete": 645, + "domContentLoadedEventEnd": 613, + "domContentLoadedEventStart": 610, + "domInteractive": 610, + "domLoading": 43, + "domainLookupEnd": 14, + "domainLookupStart": 14, + "fetchStart": 5, + "loadEventEnd": 648, + "loadEventStart": 645, + "navigationStart": 0, + "requestStart": 14, + "responseEnd": 32, + "responseStart": 27, + "timeToComplete": 643, + "unloadEventEnd": 32, + "unloadEventStart": 32 + }, + "url": { + "location": "http://localhost:8000/test/e2e/general-usecase/" + } + }, + "processor": { + "event": "transaction", + "name": "transaction" + }, + "service": { + "name": "apm-agent-js", + "version": "1.0.0" + }, + "timestamp": { + "us": 1533117600000000 + }, + "trace": { + "id": "611f4fa950f04631aaaaaaaaaaaaaaaa" + }, + "transaction": { + "duration": { + "us": 643000 + }, + "id": "611f4fa950f04631", + "sampled": true, + "span_count": { + "started": 1 + }, + "type": "page-load", + "user_agent.original": "rum-2.0" + }, + "user_agent": { + "original": "rum-2.0" + } + }, + { + "@timestamp": "2018-08-01T10:00:00Z", + "agent": { + "name": "apm-js", + "version": "0.0.0" + }, + "context": { + "http": { + "url": "http://localhost:8000/test/e2e/general-usecase/span" + } + }, + "parent": { + "id": "611f4fa950f04631" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "name": "apm-agent-js" + }, + "span": { + "duration": { + "us": 643000 + }, + "hex_id": "aaaaaaaaaaaaaaaa", + "name": "transaction", + "stacktrace": [ + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "line": { + "column": 18, + "number": 1 + } + }, + { + "abs_path": "http://localhost:8000/test/e2e/general-usecase/bundle.js.map", + "exclude_from_grouping": false, + "filename": "~/test/e2e/general-usecase/bundle.js.map", + "function": "\u003canonymous\u003e", + "line": { + "column": 18, + "number": 1 + } + } + ], + "start": { + "us": 0 + }, + "type": "transaction" + }, + "timestamp": { + "us": 1533117600000000 + }, + "trace": { + "id": "611f4fa950f04631aaaaaaaaaaaaaaaa" + }, + "transaction": { + "id": "611f4fa950f04631" + } + } + ] +} diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json index 7f03829b29c..4b15cf17236 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json @@ -63,6 +63,9 @@ "started": 43 }, "type": "request" + }, + "user": { + "id": "123user" } }, { @@ -140,11 +143,6 @@ "tag2": 12, "tag3": 12.45, "tag4": false - }, - "user": { - "email": "foo@example.com", - "id": "99", - "username": "foo" } }, "host": { @@ -203,6 +201,11 @@ "started": 17 }, "type": "request" + }, + "user": { + "email": "foo@example.com", + "id": "99", + "name": "foo" } }, { @@ -283,6 +286,9 @@ "started": 436 }, "type": "request" + }, + "user": { + "id": "123user" } } ] diff --git a/processor/stream/test_approved_stream_result/testIntegrationResultRumErrors.approved.json b/processor/stream/test_approved_stream_result/testIntegrationResultRumErrors.approved.json new file mode 100644 index 00000000000..172488d4a5e --- /dev/null +++ b/processor/stream/test_approved_stream_result/testIntegrationResultRumErrors.approved.json @@ -0,0 +1,3 @@ +{ + "accepted": 1 +} diff --git a/processor/stream/test_approved_stream_result/testIntegrationResultRumTransactions.approved.json b/processor/stream/test_approved_stream_result/testIntegrationResultRumTransactions.approved.json new file mode 100644 index 00000000000..9421fa0d0b2 --- /dev/null +++ b/processor/stream/test_approved_stream_result/testIntegrationResultRumTransactions.approved.json @@ -0,0 +1,3 @@ +{ + "accepted": 2 +} diff --git a/testdata/intake-v2/transactions.ndjson b/testdata/intake-v2/transactions.ndjson index 555f0e0f31f..174e6dc3046 100644 --- a/testdata/intake-v2/transactions.ndjson +++ b/testdata/intake-v2/transactions.ndjson @@ -1,4 +1,4 @@ -{"metadata": {"service": {"name": "1234_service-12a3","version": "5.1.3","environment": "staging","language": {"name": "ecmascript","version": "8"},"runtime": {"name": "node","version": "8.0.0"},"framework": {"name": "Express","version": "1.2.3"},"agent": {"name": "elastic-node","version": "3.14.0"}},"process": {"pid": 1234,"ppid": 6789,"title": "node","argv": ["node","server.js"]},"system": {"hostname": "prod1.example.com","architecture": "x64","platform": "darwin"}}} -{ "transaction": { "id": "945254c567a5417e", "trace_id": "0123456789abcdef0123456789abcdef", "parent_id": "abcdefabcdef01234567", "type": "request", "duration": 32.592981, "span_count": { "started": 43 }} } +{"metadata": {"service": {"name": "1234_service-12a3","version": "5.1.3","environment": "staging","language": {"name": "ecmascript","version": "8"},"runtime": {"name": "node","version": "8.0.0"},"framework": {"name": "Express","version": "1.2.3"},"agent": {"name": "elastic-node","version": "3.14.0"}},"user": {"id": "123user"}, "process": {"pid": 1234,"ppid": 6789,"title": "node","argv": ["node","server.js"]},"system": {"hostname": "prod1.example.com","architecture": "x64","platform": "darwin"}}} +{"transaction": { "id": "945254c567a5417e", "trace_id": "0123456789abcdef0123456789abcdef", "parent_id": "abcdefabcdef01234567", "type": "request", "duration": 32.592981, "span_count": { "started": 43 }} } {"transaction": {"id": "4340a8e0df1906ecbfa9", "trace_id": "0acd456789abcdef0123456789abcdef", "name": "GET /api/types","type": "request","duration": 32.592981,"result": "success", "timestamp": 1496170407154000, "sampled": true, "span_count": {"started": 17},"context": {"request": {"socket": {"remote_address": "12.53.12.1","encrypted": true},"http_version": "1.1","method": "POST","url": {"protocol": "https:","full": "https://www.example.com/p/a/t/h?query=string#hash","hostname": "www.example.com","port": "8080","pathname": "/p/a/t/h","search": "?query=string","hash": "#hash","raw": "/p/a/t/h?query=string#hash"},"headers": {"user-agent": "Mozilla Chrome Edge","content-type": "text/html","cookie": "c1=v1; c2=v2","some-other-header": "foo","array": ["foo","bar","baz"]},"cookies": {"c1": "v1","c2": "v2"},"env": {"SERVER_SOFTWARE": "nginx","GATEWAY_INTERFACE": "CGI/1.1"},"body": {"str": "hello world","additional": { "foo": {},"bar": 123,"req": "additional information"}}},"response": {"status_code": 200,"headers": {"content-type": "application/json"},"headers_sent": true,"finished": true}, "user": {"id": "99","username": "foo","email": "foo@example.com"},"tags": {"organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8", "tag2": 12, "tag3": 12.45, "tag4": false, "tag5": null },"custom": {"my_key": 1,"some_other_value": "foo bar","and_objects": {"foo": ["bar","baz"]},"(": "not a valid regex and that is fine"}}}} {"transaction": { "id": "cdef4340a8e0df19", "trace_id": "0acd456789abcdef0123456789abcdef", "type": "request", "duration": 13.980558, "timestamp": 1532976822281000, "sampled": null, "span_count": { "dropped": 55, "started": 436 }, "marks": {"navigationTiming": {"appBeforeBootstrap": 608.9300000000001,"navigationStart": -21},"another_mark": {"some_long": 10,"some_float": 10.0}, "performance": {}}, "context": { "request": { "socket": { "remote_address": null, "encrypted": null }, "method": "POST", "headers": { "user-agent": null, "content-type": null, "cookie": null }, "url": { "protocol": null, "full": null, "hostname": null, "port": null, "pathname": null, "search": null, "hash": null, "raw": null } }, "response": { "headers": { "content-type": null } } }} } diff --git a/tests/system/error.approved.json b/tests/system/error.approved.json index ba7a63f4d91..a3d51874020 100644 --- a/tests/system/error.approved.json +++ b/tests/system/error.approved.json @@ -110,6 +110,11 @@ "transaction": { "id": "945254c5-67a5-417e-8a4e-aa29efcbfb79" }, + "user": { + "username": "foo", + "id": 99, + "email": "foo@example.com" + }, "context": { "tags": { "organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8" @@ -162,11 +167,6 @@ "my_key": 1, "some_other_value": "foo bar" }, - "user": { - "username": "foo", - "id": 99, - "email": "foo@example.com" - }, "response": { "headers": { "content-type": "application/json" diff --git a/tests/system/test_integration.py b/tests/system/test_integration.py index f0b059c2f34..4e7bc9fa9d0 100644 --- a/tests/system/test_integration.py +++ b/tests/system/test_integration.py @@ -190,20 +190,19 @@ def test_enrich_backend_event(self): assert "ip" in rs['hits']['hits'][0]["_source"]["host"], rs['hits'] @unittest.skipUnless(INTEGRATION_TESTS, "integration test") - @unittest.skip("WIP") def test_enrich_rum_event(self): self.load_docs_with_template(self.get_error_payload_path(), self.intake_url, 'error', 1) - rs = self.es.search(index=self.index_name, body={ - "query": {"term": {"processor.event": "error"}}}) + rs = self.es.search(index=self.index_name, body={"query": {"term": {"processor.event": "error"}}}) hits = rs['hits']['hits'] for hit in hits: - assert "ip" in hit["_source"]["context"]["user"], rs['hits'] - assert "user-agent" in hit["_source"]["context"]["user"], rs['hits'] + assert "user_agent" in hit["_source"], rs['hits'] + assert "original" in hit["_source"]["user_agent"], rs['hits'] + assert "ip" in hit["_source"]["client"], rs['hits'] @unittest.skipUnless(INTEGRATION_TESTS, "integration test") def test_grouping_key_for_error(self): diff --git a/tests/system/transaction.approved.json b/tests/system/transaction.approved.json index 21cb74900d4..cd99aebdbc3 100644 --- a/tests/system/transaction.approved.json +++ b/tests/system/transaction.approved.json @@ -50,6 +50,11 @@ "environment": "staging", "name": "1234_service-12a3" }, + "user": { + "id": "99", + "username": "foo", + "email": "foo@example.com" + }, "context": { "custom": { "some_other_value": "foo bar", @@ -62,11 +67,6 @@ "my_key": 1, "(": "not a valid regex and that is fine" }, - "user": { - "id": "99", - "username": "foo", - "email": "foo@example.com" - }, "request": { "cookies": { "c2": "v2", diff --git a/utility/map_str_enhancer.go b/utility/map_str_enhancer.go index bc8c19e9b73..7da76186e01 100644 --- a/utility/map_str_enhancer.go +++ b/utility/map_str_enhancer.go @@ -159,6 +159,16 @@ func MergeAdd(m common.MapStr, key string, val common.MapStr) { } } +func AddIfNil(m common.MapStr, key string, val common.MapStr) { + if m == nil || val == nil || len(val) == 0 { + return + } + if _, ok := m[key]; ok { + return + } + Add(m, key, val) +} + func MillisAsMicros(ms float64) common.MapStr { m := common.MapStr{} m["us"] = int(ms * 1000) From 10ed047758a4e33d7bb055511826178be56bb37f Mon Sep 17 00:00:00 2001 From: simitt Date: Mon, 14 Jan 2019 13:38:23 +0100 Subject: [PATCH 2/8] Adapt dashboards. --- _meta/kibana/6/dashboard/apm-dashboards.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/_meta/kibana/6/dashboard/apm-dashboards.json b/_meta/kibana/6/dashboard/apm-dashboards.json index 30a031fc933..50770010193 100644 --- a/_meta/kibana/6/dashboard/apm-dashboards.json +++ b/_meta/kibana/6/dashboard/apm-dashboards.json @@ -85,7 +85,7 @@ "type": "terms", "schema": "bucket", "params": { - "field": "context.service.name", + "field": "service.name", "size": 1000, "order": "desc", "orderBy": "1" @@ -149,7 +149,7 @@ "point_size": 1, "fill": "0", "stacked": "none", - "terms_field": "context.service.name", + "terms_field": "service.name", "value_template": "{{value}} ms", "split_color_mode": "gradient", "terms_order_by": "61ca57f2-469d-11e7-af02-69e470af7417" @@ -226,7 +226,7 @@ "point_size": 1, "fill": "0", "stacked": "none", - "terms_field": "context.service.name", + "terms_field": "service.name", "terms_order_by": "_count", "value_template": "{{value}} tpm" } @@ -394,7 +394,7 @@ "error.exception.message", "error.log.message", "error.exception.handled", - "context.service.name" + "service.name" ], "sort": [ "@timestamp", @@ -468,7 +468,7 @@ "error.exception.message", "error.log.message", "error.exception.handled", - "context.service.name" + "service.name" ], "id": "ceefd050-7c6a-11e7-aa55-3b0d52c71c60", "panelIndex": 2, @@ -592,7 +592,7 @@ "type": "top_hits", "schema": "metric", "params": { - "field": "context.service.name", + "field": "service.name", "aggregate": "concat", "size": 1, "sortField": "@timestamp", From f590575fecd0032d904859580e9ab957379abdf9 Mon Sep 17 00:00:00 2001 From: simitt Date: Mon, 14 Jan 2019 17:48:23 +0100 Subject: [PATCH 3/8] fix user_agent pipeline --- model/error/event.go | 4 ---- model/transaction/event.go | 4 ---- .../testIntakeIntegrationRumErrors.approved.json | 3 +-- .../testIntakeIntegrationRumTransactions.approved.json | 3 +-- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/model/error/event.go b/model/error/event.go index 274168f8a77..1df45fd9eac 100644 --- a/model/error/event.go +++ b/model/error/event.go @@ -224,10 +224,6 @@ func (e *Event) fields(tctx *transform.Context) common.MapStr { e.data = common.MapStr{} e.add("id", e.Id) - if tctx.Metadata.User != nil { - e.add("user_agent.original", tctx.Metadata.User.UserAgent) - } - e.addException(tctx) e.addLog(tctx) diff --git a/model/transaction/event.go b/model/transaction/event.go index dcb3e311cfe..bcb92c2aaeb 100644 --- a/model/transaction/event.go +++ b/model/transaction/event.go @@ -128,10 +128,6 @@ func (t *Event) fields(tctx *transform.Context) common.MapStr { utility.Add(tx, "result", t.Result) utility.Add(tx, "marks", t.Marks) - if tctx.Metadata.User != nil { - utility.Add(tx, "user_agent.original", tctx.Metadata.User.UserAgent) - } - if t.Sampled == nil { utility.Add(tx, "sampled", true) } else { diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json index 42cb7269e6b..7ebcf4a0d29 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumErrors.approved.json @@ -101,8 +101,7 @@ } } ] - }, - "user_agent.original": "rum-2.0" + } }, "processor": { "event": "error", diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json index 61bb52b1eb8..b208ec9ccae 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationRumTransactions.approved.json @@ -58,8 +58,7 @@ "span_count": { "started": 1 }, - "type": "page-load", - "user_agent.original": "rum-2.0" + "type": "page-load" }, "user_agent": { "original": "rum-2.0" From 06e7f30861630abb29979da9e1e1bf0f79b48a9a Mon Sep 17 00:00:00 2001 From: simitt Date: Tue, 15 Jan 2019 09:03:16 +0100 Subject: [PATCH 4/8] Revert "Adapt dashboards." This reverts commit 10ed047758a4e33d7bb055511826178be56bb37f. --- _meta/kibana/6/dashboard/apm-dashboards.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/_meta/kibana/6/dashboard/apm-dashboards.json b/_meta/kibana/6/dashboard/apm-dashboards.json index 50770010193..30a031fc933 100644 --- a/_meta/kibana/6/dashboard/apm-dashboards.json +++ b/_meta/kibana/6/dashboard/apm-dashboards.json @@ -85,7 +85,7 @@ "type": "terms", "schema": "bucket", "params": { - "field": "service.name", + "field": "context.service.name", "size": 1000, "order": "desc", "orderBy": "1" @@ -149,7 +149,7 @@ "point_size": 1, "fill": "0", "stacked": "none", - "terms_field": "service.name", + "terms_field": "context.service.name", "value_template": "{{value}} ms", "split_color_mode": "gradient", "terms_order_by": "61ca57f2-469d-11e7-af02-69e470af7417" @@ -226,7 +226,7 @@ "point_size": 1, "fill": "0", "stacked": "none", - "terms_field": "service.name", + "terms_field": "context.service.name", "terms_order_by": "_count", "value_template": "{{value}} tpm" } @@ -394,7 +394,7 @@ "error.exception.message", "error.log.message", "error.exception.handled", - "service.name" + "context.service.name" ], "sort": [ "@timestamp", @@ -468,7 +468,7 @@ "error.exception.message", "error.log.message", "error.exception.handled", - "service.name" + "context.service.name" ], "id": "ceefd050-7c6a-11e7-aa55-3b0d52c71c60", "panelIndex": 2, @@ -592,7 +592,7 @@ "type": "top_hits", "schema": "metric", "params": { - "field": "service.name", + "field": "context.service.name", "aggregate": "concat", "size": 1, "sortField": "@timestamp", From 9280b559d005d07f8085d144598731b7bbe3dfb0 Mon Sep 17 00:00:00 2001 From: simitt Date: Tue, 15 Jan 2019 09:07:01 +0100 Subject: [PATCH 5/8] revert fields.yml changes for stored objects. --- _meta/fields.common.yml | 4 ++-- include/fields.go | 2 +- model/error/_meta/fields.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/_meta/fields.common.yml b/_meta/fields.common.yml index eb18df73d62..e245630962a 100644 --- a/_meta/fields.common.yml +++ b/_meta/fields.common.yml @@ -135,9 +135,9 @@ open_link_in_current_tab: true url_template: - min_version: "5.0.0" - value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(query_string:(analyze_wildcard:!t,query:'service.name:%22{{value}}%22')))" + value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(query_string:(analyze_wildcard:!t,query:'context.service.name:%22{{value}}%22')))" - min_version: "6.0.0-alpha1" - value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(language:lucene,query:'service.name:\"{{value}}\"'))" + value: "../app/kibana#/dashboard/41b5d920-7821-11e7-8c47-65b845b5cfb3?_a=(query:(language:lucene,query:'context.service.name:\"{{value}}\"'))" overwrite: true - name: version diff --git a/include/fields.go b/include/fields.go index d3fb859bf91..7a248951f7f 100644 --- a/include/fields.go +++ b/include/fields.go @@ -31,5 +31,5 @@ func init() { // Asset returns asset data func Asset() string { - return "eJzsvXtz3LjxKPq/PwWutnJt54yoh+XH6tSeHMX27qrWD/0sO5v8kpQGQ2JmsCIBLgBqPJuT734LjQcBEhzNSBqvc4+cqqzNIYFGo9Hod++iS7I8RiSXDxBSVJXkGL1+ef4AoYLIXNBaUc6O0f96gBDSP6ApJWUhswfI/u34Afy0ixiuyDHa+d+KVkQqXNU78ANCalmTY1RgReyDklyR8hjlXLgngvzaUEGKY6RE4x6Sz7iqNTw7h/sHz3b3n+4ePvm4/+J4/+nxk6PsxdMn/+1mSICq/7zCiuxpcNBiThhSc4LIFWEKcUFnlGFFiuyBf/t7LlDJZ+YVidScSkQlfFUMDbTAEs0II0KPNUKYFX44xpV5m5rXBMHhbB/sig0W0ZQLhMvSTp7FOFV4JgdRZ7B7SZYLLooe5v7+j51a8KLJNW7+sTNC/9gh7OrwHzv/vAZ3b6hUiE/dwBI1khRIcQ0MIjifG1A7kJZ4QsrrYOWTX0iuuqD+i7CrY9QCO0K4rkuaYwPZlPPdCRb/Xg31T2S5d4XLhqAaUyEDfL/EDE2IXwUuClQRhRFlUy4qmEQ/t/hH53PelAVsYs6ZwpQhRqQi7f6aVcgMnZQlgjklwoIgqbjeViwd6gIgXrvFjgueXxIx1hSDxpcv5NiiroPPikiJZ8PnxiBUkc89dO78SMqSo5+5KItrtrpH+MTNa4nTYsD8pN+0PwcrO2WIqzkRGsEox5Ikx4n3IOcsx4qwljEgVNDplAh9tCxKF3OazwGxSh+mqSCkXCJJsMjneFKSDJ1OUdWUitZlO4ydVyLymUo10t8u3fQ5ryaUkQJRpjjijHSW43CPZ4Q5tFrGeBI8mgne1MfocDVuP86JGchyS09Nlq1ghCe8UfBPyadqoVdKmKJqOUJ0ijBbauixJsOy1AQ3QgVR5i9cID6RRFzphZrN4wxhNOd6zVwghS+JRBXBshGkil/IHDVKRFleNgVBfyYYCHoGb1Z4iXApORIN05/ZqYTM4B6AVWV/dOuSc82+JgTVvG5KzQ7Rgqq5BhbTUmpWojwuRMMYZTM9qn6owQkWIzTfNBtu2ewc1zXRW6bXBGTlVwS8Va+TZRbpU84V44qE2+CWeqwJVY+gSVTDBEsG7lvymRy1MGaaCDT/n9KSTAhWGZyTk7O3I83RzcXgx4+XZbcX1/WeXhDNSRYQQshxCk6kYTJzzGYE0Wl7EjRxUImk/kbNBW9mc/RrQxo9g1xKRSqJSnpJ0E94eolH6AMpqCGKWvCcSBm86EeVjT5NEr3hM6mwnCOzJnQOiM8itgIU7pBq73r9dz+YOymaKChn/nmKU6GBq2rF2dF//mKGjsgni6EImN6zbD/b3xX5YRpO/f/bAPKdJpUYwuj3j1aUwACBPc6GGc3oFYGLBzP7qXnb/jwnZT1typAuDIkLt2ikFhx9b2kUUSYVZrm9ijrHTOrJ9VmLxpo0SnOEpsIMZBTNVJEkNRaGRKlEjJBCHz5muXFvumhAR7g5r/TkU8GrDj5Op4hx5A4YoMCcPPeITxVhqCRThUhVq2WW2uwp5+lt1ju4jW3+uKy724xiQgz5vZ4ASYWXEuFyof/j90Bf+tIIGJ4EJsuAP+obMotRxjzL8thv31/AWHaaCWlfAf5Np5pIouGGCSYilgrnc8pIGv12iPQe0GIbO/CJ0V8bgmihb8gpJcJshz5agIdHdAoXOtz68nFif7wEppm5Yf7w/cLtBrB6WiSX/AIfTZ/u7xfpJZN6TioicHmRWjz5rAgrSHE7BLx2c9wGB4YdaeFWVLgsl/bykQjngkutqUiFhRYwNG8YG1KnxdjfVquQM33QTugwk5e0J0q9DJ+tJ0ud2IE0hyjIFGQ4bI4VZVRRrDggAyNG1IKLSy1sMQLahGGZRkYSZIZFAbejviU5k6PgTXOFTmhBhXmASzQt+QIJkmtFyMgBH1+e2eEM52oh64GjH+jXA2DgBpCEFeb187+9QzXOL4l6JB+b8Y0wXQuueM7L3iRG59R715lOgCpNtBLixBCHDCUwkxgAyNA5r4iXIrTMrt9URFRoxynHXOzoi0mQKRHR9KyzHGmkG/uzlQfNHk6IFwADORemRRoUNnM72A4ewmx0TEssbmjNqRrZwPJbaZMyDdIvDTMoBuHTipPWZIES47SI1FJYO5omF7Mlu3CAvWIenSY73p6bSJBaEC2wwdVpbnGtaUpSYaZoDtI/+azshU8+m5M3svcqlf7CVxxdUb1G+htpdQW9RiJAf5BUNdhi/3SKlrwRfvQpLktpUAmShiIzLpYj/ZK7d6SiZYkI02K0JUfeiNzcTQWRSlOAxqNG0pSWpT5rdS14LShWpFzeUFTERSGIlNvij0DWRmewBGUntBecZxvVhM4a3shyaYjXmnNoWUbjSV4RsGehkkql9+z0bIQwKnilN4ELhFHD6GcktT6vMoT+1uLY3MfxeIpbxUbghYPNEf04sw/GBodp8QIMSq30UDTGSGJU6nFG67EGa5wZEMdaXawJK6wcCIQWDanvClBosoGbvF7zJo9eXLFHp2d+4ZY7mq1KLNcabTSIXHgtH52eXR3pB6dnV8/aDR6Av+ZCrbmCkrPZems440KthN4bcHC+DUHo7cnLtZDowDDEsA1ILAs0E3Rm/wa9JUrQXPbgmSwVSTCBdXbFCxwHL47WA/HPejKjR2tlJLxuFDc3UqD99gkIroFbQ3u4JmWZ2dYCtwfqjIRivpW0fogedkSta6D5gXBvuMJaBRFiGZqtMJI1yemU5qjkxlSLBCkdO9J33FUr5pk/XGg4YzMIEfRK37p6vcBkQw4Yoje8aBDq+CBiZDiAosnTW+dHJ/yi5rQD8Ar8IPSGsxlVTWFuzhIr+EesvHkiePgvtFNytnOMdp8/yZ4dHL14sj9COyVWO8fo6Gn2dP/ptwcv0L8fptajb3fKCFMXHTvGdavqn+dr1hTaM/ysA0t6x4Wao5OKCJrjNNgNU2K5daBfmnlg1gFYX2KGiySQgswoZ1uH8QNMswrE/2rIhORJPFL1BZBI1UoMvuVMCYLLVRtNJb/IefFFNvv0/D3Scw1t+MmKzf4ScNoNvxbM3f96mYJ0aLsTwvKNQfwkidh1cnHwptGkHRMdIWtwMtoQn6KZwKwpsdAUY90rgphroWPug+0y0qo38hnuQoW5THLCFBFWy52WnAvEmmpCBPhAwLjh9EnZGdqAWKJ6vpRU/8U5T3JHyrIHzjsO5jn9erk07ijKEG4Ur+DmmhHu1j2wYxMuFWe7Rf6gY+jgTdG1c7SP1jNzfG/u2+AaNRIAb8D/QdlUYKlEk6smdJK0iNH7EBlfzeNr/CJTK6wZs6AMjceYodcvD42bRt9yU6LyOZFm7+DOpsH0xvvUwqwv+tiFGPm9qPRmxhgIP6BomPVbCVJx5c2SiDdK0oIEc6Whw8i6YcIhQ08NfGypL/Z4mmHbocD7ZKcPHUB2ghhx6+nIIQHVgl/Rgoi19GNPjSQ/vJ0QH134sGIHiPcShi5ukh+O0CwnI8RFzGjojCpc8pzgri7gDQBXmJZ4Qkt9nf3GWcJSv2qpjdwlWKrdg/x2Kz4JwEC/gQ7svBtAkkDr7WYOLMbcJGutYAjG/srWW4C9WW4CtbP5Z7e0U3vQ6e7B4ZOjp8+ev/h2H0/ygkz311vEqYUEnb5y5AdLiPwOw/Cn/Xl3Y0nyoAXX1TrAuV/TTqibYFcdZhUpaFOtB/hbx50Cb9UacOMc5Lc7o4lnz549f/78xYsX33777XqAf2y5uIEFQgLEDDP6m3VFFj52xLo/lm3ASHxRayGAQmgDwsZwtKsIw0whwq6o4KxKW5zaC/Hk53MPCC1G6AfOZyUx9zl6/+EHdFqYCAwT9gKeqWio1kMTzBMqc5gyz+mdtNB5vJ7E4L+KLeTWjN0Lcwos8U5574KDjE3YujOsaZhPw2HAbiqJm3JOylqLzUZsMTfmBMuAaPwc0un5S82oFG21jQ2NyfbrbbGAD2Z4VGGGZ/pGBx7rl5H0gpm4rgG+tU2fqAcL0a7h2M9f4dl2mWYoR8Bs3oRgQFtgiSYNLZUXjgaAVHi2LRjbw2IhxEP35DYx1ULRats9AKJoynVAiCIrkQ9SvLjJ/QfIcUGJqMu/AhdRzMFe9X5Yj4cF363hQgw9VKCnGiPtno1JXTHoBs5Dw/XaeGf0Nbu7Ip/dvc/rq/d5Bfv1n+r4Gl7Cl/d+XQ/L9lxgIZf5T/ODhWzDeZeA733FzrAVMPfgvfeI3XvE+qu694jde8TWReK9R+zeI3bvEbupR4x4oSfKLUVr64VvicK74c3or1fF9WC/U8pKMmH1Gsp6/fLczWt20AYqclidRIpnaExymdmXxiZnRMSZovpSrRqpTIA3bFM5EJ6q//ystadfGyKWEGxrIry9QkFZQXMi0e6udSNUeOkA0giWJZ3NVbmMD4/P0QtWBGPAqgyYpZbbKFNkJmwwLC5+0WAbiS3WEPM5qbDHjb1nB5cEhuJGmCxB+w2V6ACSfyZE4UOUtM0FL7SDekIVgneMsa+DR2tn+7UW0RwSamxAsBkf1BXMluiSsiLTjEavtDLB6eYFNQ88nybvTW9NSYxfU2+iS/WDCG+Ta9lNmKNKknLaujG12KnHj7C5vlvyS2VzTG1+Xx/WoZTY6wAKUmOvgQZ2u00FTc7duRzvDBNmbj264+rG3NzHhCfXq15GxeurmySnGnpJ+Q1cNHnadVDyGTLOBUHziOoydAK/xlkaTvFxNKkXGOSGgtFpblaN24TPDL1pE5OB67lcVchXoBXRt7DzgOqneoj2a5/iyqdhirMbBLtUSQQZLy7cwYYwtHkkRutFE2KSRpwyip2NUCt2oVo6MlayRBrKhKgFIXoOF5/OChufQISdwKZzmHTXvORSr+TEofp6tDqrERdECw2gh5QwlskEgH9GScEaiDRC05m2EV5DEmhRW5GKiyXS7A9yDOxARSdD+aopGRHGEU/bXGX7mswx0wuFfOWbXfRbZV2nr/TWezu15783yB7TN0If0rsxE+tzrsePbtahxLAZvQK/affQL/S5dE7lqGqCGzEay109IzCm6wHs6QnEN6dNm+sshK11xEaDav40hjfGIzSWCiui/4JLLKpxhn7GQh8ASPKeNhAe5aUTPtXSyggtYtGjLjEYkWy8ixaebeELnOekVpANa0NfzO3kJJwRqkuCJTDMaEhwHuS46QrLnhAA7oELxubqbOWSMXzCzjC0/V5kmNPZ3OY+pW+AgZ07jemASsOIINFKb/scM7uHmUlGG4+cM0ASJm02UquM4JisLPgtnF6WxS4ZbQ0yiDeM3AEZRCM2kiTIIEULjdY1wcEMPDZNFWZl26AJSFc2N1OOawWc12Yir2QSXve0+YctfVAWE4MngPbgz3FsgbTU4LZ2HFwvcOCB1+/iotBn3V7Yu3Bhk2Icb+V4Skuymwuir8+xcXOZejBUtvmu7v60K6V6rgoU7uR5hT2qsZQar7smZS+9UbxROd+e01ivxk5xHSs/DX4Odgszu92jgIRlHJ3ZzhAbU/SxdOmj7f1vXrY7JZs8B18elLWZYlo2gsSMORpzmElvciLjIQeZ9Jon0q4hvcHbKi3wgYAEaARvi5UmoYjoP2dmRfiKQzyUD0xpC0lpggUz0pAKxYum3HolDDOLtVWlakL0VmYS00NmEn0RjCq9jcrk8HPhK5okj3C1lL+WaWRo0CRZ11N6Y2zYaYbMGZxpojYWxrF9d4weaXYmiUJ7VsqWRD3WWIlXr/WA2KDSTPRXWjg36AJOHJ3yEM0++9haVTr2HlvRirIWCFMdB0xR/pHdb03ABuqsazaPJKCBEybJFRFUrSsBDXkYd57vrLdH53a+zpXmwOgINz/PrdE3HXbov7KiQkXARcg0hwtCFb0W6Itl6f15KFFTI8U7XDe6nzRHrPAlQaBT2emoZb85Z5JKBVqlsfMlTWj+sjJ5/uWNKf8b9EkTkWoYZIRbm6YNF6emrpGc8wUzcYG5KpdoSZQm1/+DCm4q5HFxGQ2p5QfN2yVakCgw5Rt0KtH/+83B4dH/dHGJcbq93qr/A9X2uLjUgMCJAktGayOLBjTBpDS/lEkq3TknNTr4Fu2/OD58dnywb8JoX77+/njfwHFO8kZvt/lXtG9657QUYkQ7Yd44yOyHB/v7yW8WXFTuApo2WlSRitc1Kdxn5r9S5N8d7Gf6fwedEQqpvjvMDrLD7FDW6ruDwyeHax4EhD7gBdjLfNU2PgXfgfDk/8lG3xak4kwqgZUxBBk7L1UprcKydXM7WaqgrCCfibFlFzy/CHILCir19heGY2GmX5+Qzoim/BspTIUS6qspCc2MiPebjy+MfWYcbi/MfYymuIyE9haM8LfeoZljOb+VeNdSVxszn/rbyZ9fvlp7537Eco4e1UTMcS2hkhnU9ppSNiOiFpSpx3ozBV7YfVBcowtkqA7DQWtvrr9AG9GNKribWKNXduCIB2sGwTDjkuScFSn3wOnUkiuoCEBj5t+EFUBil0zzJOBWRjdoI8u6ngnHsnPieTZAwgztmhnaCOa+vEgrsnaSy400An+02kUEFfiiaqUPJfK1WdvKc9ZgF986FuxY8y8FwcUSPSLZLNM6FG5Khc6XUhOJH1g+NndZNB6vbSEdCJZfUJmSa09aud7Pb2YHznCMsD7mnIH58vSVhWPndSN4TfZOKqmIKHC18zhWCfFkIsiVsae6T84/7jwGEy1DP/54XFXt1Uxx6d7a3X96vL+/062g5E01Rslck+qLsMjlyi21yrAZvZc3l6xAa18ekqjbTdeSOJWKstxasP938JstFxM8cpP3JBKrhMPtaV/OXBlRAFWaunQtVTgOnZabbA2gDjCG/ZSUGUmzs3BqSuqGtfCiMSfLoAyaIIbWwdWU4zJD43adY+NZCCtz+t/irfmsBM6Vu15CCEedffPA+iVQVwI43h9baS030bN1reUoDg4HfQMbo4xWgIyHL7E5PZ7VvpKAN/Ro6Ala7tiFvE+U19CaK1EH+Is3X+Pf434UrqLlWm3Nu75OoNnsBix008Nm2Pi1R82anDTjSCIJ54peaelf42lKhVSuounQwshGNv9Nl6VvqWsXBVOFS/LLiEbUSyrx9SsSVF5eyA4LXMUYpyXHa3poP1B5iWBsU+SU8p6GZnm3tII5krwEc4+rg+f+fJLElMwytcgeSq8NWZFAn7Zrl3jBuKg22MAN1voObJX0N1LAfNcse+TdZSVI7fuahxzs7w/aWCSqMGUm1MfUF4XiYFofrUy0PmbgR7S12ozxT0o669wGLXASyp/DMAtsatVIQhC2ZldYisGtVU5xWboKdAkH95R6ft5xZlt39/ftC0N4PIFRuh5TZE0jsQ8LnM4STbSI51ihdeTq5xBs49ySYN8AyDMAw9UCd5cclpLntK2BDHqjqxYYlbYzSNuzNhPnQwUiHiE155LYiujGWg2TnTp5HL3ljCoO18Pfvz99+09XPR3sYTYjHQoKQviIMfU6e2o/pwZPp8RcFvr17hpUUDzfGn028si2AeSqVaCGDkxaEo62+QxroLjN2S/jw9oWzhczoi7uas6PMBwsAcQOuaxKyi5lcm6YIIoxu8XMIXOA3fSj9444HHCfjVPyBSJYLjWOFAFSmSwtsbkhAuuH105rq6R1ERrav2+xHlgDOJPBxDlCBRVw1ixKHydRWpCoiMMt5n8FIw0kua4kKcrCGKBbgHCqB2pNWC7gx3As5v9u+UwKlCaIbbgj2tLyKHgPtH716fTVY8NJ7G0aRGo9OocfW2QhvmCdEmre0LgIE4tvSzUw2kMwgYte7qRP+7gb1JwJWmGxNLwNcPJDZ9np2aOUjDubP6xEMDh3dXPy9Id//9nRfhqgt5pmw12nDPFc4bJji02CJulv64IWGYn6NKBH0lND+pRmIda2yLVIg4vCqTFjPdoY0VhmASfxOM1iqiihfDWQkTweAflGS8oQTAVIspESIERXvNAnqEjOnm9j9ooobGLKwXNdJIStkGBdjlTwaP1oQkOoQTRhRaws2EbCwjvSipRCs8CSXGHWiwyOIqnuIOrrbixuw0GrZu2ufDqw7b26xEpLmb9DhnnofATQEvseNAOw2/5j+2Tdotyu6EwkY9u6yijnVd0oE9Voq7ZA1DhE9AXNQxK2y7B7SCulml4hLAhRjFuEmJoc7PoQRr1SwGsbszjHolhgQUboigrV4NLVTJEj9AoKOwRFLIy681MzIYIRBcbUgtw0T1yvKk0Mt/dC/2jHDovBpMw3KigI76wGC+fvHDsIx3pLK710QVQjTGWuNWvMbGuF79ZaHaRrWhsfrCtYU7CWT5DabvRSm37TlB2P+K8NLoGLu6R4PYoL+tXA2GCnNsZISysmHEnqs90pm0VyWvheR0ZJVlx/M5Sfvs2gVnOeUxa+E+kJ1XnybM8JU/5mBAYE68zz/F1fAZTNpk1cZoAyY4FZqx7PcZT00Tjv5Bi6NcAWZn0k3XUSP3AMWrvU8y+b8/6jPV7XzL7t3icDx+t7LmxlJFc4zvbVsBaRqGyeHgoaF419aatxbJ47naKrauTq7QSZcp79jkK7f1CHKTDqRCO2RLgG4fm4S5HPqSJQaPHGSG0dvp9fPLt4drSmU/d9TQRWbQunCJhEojsPZVx7mbdjnMMYwRubJb3rw/f+vNvCLB0WzDuAhzsrSAPe/eNodMXrC4vTrldeo68Gq1T8ya7vFdZ53GtvtAus9yJs5oZukjvvJLlo8C0kn/b23U2MHkHvrpwwxeUINZOGqWaEFpQVfNG1b7f1qLBYULbFTNqWvN/iXBPJX3dusVhzj7qMAU1ONjY0XF5iMfqK3sZi3vJf8BW5/YqMhOlsPD7R0eZ8GTNGalm4oh3R47YLK8iEYrbJis4tGJYAoZVpMcdqhMxYI2jKOJFFSIyJxfRTbm+/moP97OAoO7jNBrnNALVF4AWSSkDtzMQSLrWsf7eEdpQdZfu7BweHuzZD4jZrMfCtsaT78iiJ3b0vj3JfHiWG9b48yn15lPvyKB0Q78uj3F15lLlSHbv7jx8/ntknN20XoIfwUTw3La1rughmFVFzvjVj+o9K1W4qZKYaSJAxLh5jGoNovQkJA0sURyVfEAEBaFMufMWTDJ2T+ETsvPEvvsQ1VXoE2Lkd53bdOXUJF1q0ev3yfAchafL3k3kCM6JGqIaM9roZSOF0+JzwYplZf9C2sPrR2iyBujx6YeYU+KZR/IKLciA13cEOnSDFms0JbpQEZ8Zvc/iAkt30Kdj1CuXx3t6k5LPMPs1yXu0NrUTWnEmSSYVVI7vc/LrVrB+6bgnbzIbMbD2G7ldxtH90Dby/B9lY4G9ON4M1lu6QeSTMA0G9n4MYsOE6nP54putx3gFFfOQKlx3HtZWY3Ql9pFENWsGc4IKI2KjTLuvoyfM1mMz2lnK+ahGD5PLixSDUjsh/H+RbOr8D7IeH9Yuj/7rjGuG/VXlnsfjxxj9YLW4YVxWO8vp5UGLnhmIHYKmPtdv7L97wWSuJurj8oeR5U1Y7qkHw88mHd+MRGr/+8EH/5/Td9+/HSTS//vAhvbRbp1sO5yWCQAtuu7dLvbDQhLRRutsgGjsXhQkhBmu/C5vW+HR5g7gbeA7XSvBGNNyETE19iJIqEymgUAMpIL60R41FshLcqfHoCuzryqGxncLWE7eEGvp+ofGyS4yo48wCFJKHHSksldCplGAXP+otsOPOMs7nOb4iPotKahozwUC5K5BX1yUlhfGNEZZzU8BcIEYWsVJHGZHQDOvKyL55STCD7OEY9KH4702TMZHkNsvyYS8bU0va4Oh2BnuQ0ddKyIxYkY2LjtnRu+jh+nFILsi63yk+51XVMItzE8rLr4hwDM3Gl4g4TNtGl9hG5/anG4WvuGF9rkg3ztpZQG/IQLceUTSjV0TfPdbPByULuVOPZKumOySlGNgPICn8TKc0vYhtObFPjX73/vwUAhlLc7AXoa3BEhx6g5dEZIjWV0cj/f/P9P9Lko9QTasRIir/KvXUVWqqXksa3xQzfGHsJ9uiHYROT96doDPBFc95id7BbOiRU+AWi0Wmwci4mO2ZRBMoTbdX2y92DXz9B9nnuarKjv8ToXOFWYFFAWh3pWPct3CQqUS4pDNmKg2Y0/eOqO9LvtC8sDOehOfOygJ5joZlNDblLbW+5D48GyB6gZncoGfDZo1CoFyH9Kcy2HGbQ8+kIritJ0PQT2b80PoWDenhRaU+K+hRU9QjpPLanJddmlc1HJTs8Vd5VFaeFZXX6V2CO7rnJrrTo3JiUG4YrfGJBbNaynWZRmJClcCClkubnmVqCMU7NadsJo1YUdFccJcaZLYel5K3mafhy/JyWZMRovmvcUr1FOdkwvnlCKkFVcpEtoWc1FlIJVWNFW7aCrVXhBUdCNt0JZ8nTHJeaMHDupx9AqsRIPYKfYOcnplsABmDp4lSQkzQggqXQ/512hVX0SCmVZoGHRfbip703F+Bbhrj3kHkcwaWoREqgW/8gnNNAJ4LuNf/8xDtjfA9TBdUkK3V3nvlBnc6h5MNlcDTqUuviz75QLT4alJ2WzH9uHNV/RFRNuFN7wr7I+KNSv9AmSIiVk7ND5qlJX9oGJTR6MMIBccrXNdBqWpbLVfL1rvQFBBVbeqirTM88sIziGUxwzGlzRwP0OM8lAgc7xp5V5QshkqfpyFxqOYC1UTQiigihiHrcJcAyi5kEUj6vxBp6JPu3VRp+SzYtB4lTrlYYFGQ4mI7Ya1BgyqfCG4z4oKfrNJfC/45bWQ6+PYwO8gOssP0KqzypZYX20vQOIEaPaamNMAPem3QMuj0zBQ8ttcEtvIf9mvrMlfUevxi9THzphCMFOflLp4xLhXNkbTSZ9iqNKboki9SFo03BAtmcrCx8u6NGVXzZgKODb3VUJR/zyNzlxa7siZ5ckceHhzP3/8P+e7ox//x9oenb/+292J+Kv569mt+9N//9dv+dw9jELbSqepaw6yxZMJVAh4gwPWEawXa8ciBQj9j2/gJRrBlJ8NWYO65q/ozQmMnAtufDElTgWRTJRH45NmLgWv4Nq2wrsWJHf1WWLFjJPDS/pLAjP/xWtwcHvXtOJ3AXBeKHD9dM7eI+dH6Sfw1ySkuHW8d+SxVk4bRCsw2a9h3Di6IIrkauZHhdZPwf/1Yu07/s7dJUADRyeVOBMYob6TilU8qMuNAS2nIE7Hr6lQe4GxKZ1CGV3EkGrbBOiWfKj1RUJ3VJTZNqSALXJZypG960UiDF2WoaK8WsB4YxCW+uDsruA4lYZILOUILMolmDoaH6IySS4lSg2p8nZy9tWu35jS3xaE9DZflCnOalZfMsBDxgdlyZFBpViX9/kpXYMHssWwv/xWo7BY6QG+tZfvXhjRmSPT64xvIbuMMSMFdEbY0Utynw9KIr0MElRoLAnXu7eqhI+brl+fZDdpzfLk2i72o+y/YMdPTSW/yL5k9NwxFT6+9Mxg8EzRTRF24E2DcrrPRqpyUFo6O172t3iooLrdsS/RgmNls5FcfmK3lQs3j7vp+e1yd33UqHRNhc+g0o3Q3m7NTtiMuayKzvkMyGmzslAMxHqGxY8b677SQ8J9a2tLpn5fwF16W5mXD0vXfWrac9mu6Ye8zj+4zj+4zj+4zj+4zj+4zj+4zj+4zj+4zj+4zj3p4vM88CuC8zzy6zzyCP19V5hEXM8ys49R+6HS3/i/rB96Fw7rrmDBB87lBH9jxhvrJVTVmS33pGsT4gUO9uhMvl8U9d+ekrKEELRYCs5nrRqNsP6SglQ1mJvARQtlsw0wbburnDRdz04jmbQbkhTsVsMIeDL9HNbQQd1lMeZ2O4AO2gvVp7rb2gb5tYNAukLIJJC0CPXtAwhqwISUl7AB3S013oP93tf/kQm59JFZr/pss8Rqtvwd6R9u/A9D7ev7m8K+j46eX09Xyb7Ogvn6/aiU30u2Ti7iLNLOVev0mGzKoACdB72n1t4F8pT6/yRqu0+VR1+VrfV4xWz+LHt6kf/4gM/dtu7OBLzFrJQHoPQYBO84LF7W+g5h73wacFnsRd7LhQmFKhblzXB/SrKbFGPGpIgxJhZfSxaC5bt2mEb9WuIOYppzX1JgdoDpnySe4DPo3OpADoW7Tu2LtCoHrxyWceRzFHN+29LN9sb6oAORASrA4ZPO4oNUI0uIzgeJ0M4ErK9cLJGlFS5wOxxpcUJ1E7h0k9rnV1BiqHPZKMLZl6WabZBbeCKNYzJoq0TxQ/3mLl1pBMnK1IeNacEVyBSECVNErkvZRBuj9+46U850R2tkt9f9r4Uj/17W1e7bzz/TiyWeSN9AlalsoOJlA1xBikoPsGXUMop0+uaq9Roq9CWV7g9QD3HHbuweTDATi6pXA7yOTg2YOiHKNiLD0azVxvy8xMyHiYfem2CcWlGJEGE0EX0jwzrp0PguQw+WCTFAN3Y1cu1EtmrPBnjLQSbHIbnPq2lT7w6O1PY/QXur01QBUt2xK1N7bh/sHz3b3n+4ePvm4/+J4/+nxk6PsxdMn/73m9f3RtquKyNS2KhoAfcHFJWWzCxNHlmw3fxMJZG/OK7KHy7BHw7WgW1iQh8VZc6MrPhI3rNU+Fjc+RA/XFTfa7nnEdCp35cqnOKclVVpsqOkVB0LGgjes0NICJaZTRNtjGbm0YfhNdvvL2KwGSQh0SK8wW2r1Kidt2M/HcFI/pul0CZEERrGuRghyEX0AuDlU1EoNsuYMNAGb4tmKxWOLtizw759A42FBFAn7trahN0SOggTaCUENK4gA1dYHWImRDbQdhVG2I5SXFDoTuZe0COQiDMNo5gydmuZDdlm4LCFEV/EWZFqPR0aYwyBdMYsXQAq2qTKnZ0gJekVxWS5HiHFUYaUgsxNiLRRMgAV0DV36/IJwkmOcTbI8K8Y3rTqfCIIaPEjrBkKdlD5nXaMFSIi7EradBPYgDKcXgXl+g/hL+1EijdZSGlTcDeLpc86YTWqAS8FEwAkyw6IwIYQSOs6MgjdNqs6E+qhWLQubZLuci0KazoIfX575lkmmQbODzICTE6r/bTFFGYVWjud/e2cjaR9J37dDD9VOb4Y31YN9fmB3DlvOvlz2F9/J3GDS9cgHdmBDHxHOVeNMuKZDHhEV2vEj7ZgeCVMbReRmZh1gpashDj9bdcfZmxPpxq52cG4YmOwMHsJuW/yeR0Nj6ENvIG+DMSkEqv7SsLzVocxxt9+lhmlRyLgKBtN0YrZo1xjsk02rX5rh9xzwcbsRo/LhQvPxCjNFc5e74Vy7n03zi1Hb61wriNOm1C9cUb1E+hsJLM0M5USA/tkmsTlWJfzoU1yW0rfOzLEiMy6WhlfZrHCpaFkiwqBjN7w2kJegkTSloKfguha8FhT6at+QGVkWvi1R04Skmb6IZkv8nWFKBzh+UU3orOGNLJeGdm0rSdoJm5FeV4MgOPCsjxB2BfaBzzdQmp9rWskQ+luLY1OFPh5PcZtvKPCiTWAxND/O7INx6LzvyiZMXxptbn/RmABho/GM9aWkwRpnBsSxvv/0DQZFG2wDimhIaKirxYwhM/32Y2jD2NXo1Zfmfu94QtDp2dWRfnB6dvWs3eAB+DdIXt5AKeZCrYT+ywdBrwTDEMM2ILEs1UzQmX0reTttVteLo/VA/DMk8kCXnzZh10ayGt3PXBNDBHSbjJoW2jUVvDObYbMOuD1Q78OX7sOX+qu6D1+6D19aF4n34Uv34Uv34Us3DV+y5UL6Jo724foBJK72SFefVuFvXEAwkb43295yJqYJh569soQIkaHApCllhS2Q5/ySUEzIWLLcHe/Hc9PrLzoJCXfQEvHOeoYFAUCuIGXDmLH4wAKGKtHRwmlYpoVY6bvMLg01uu/N6xW+JFIrUTWXksZOIASV8GKsBgm6ZgdZULByGDTfdcyZJgWB0B9BCcvBpyFlQ6SxfOgxBSn0YmyLQ9D/owG1SGfj0Fy3cVq4Fuk+O5QVLS0YSwFlM2iyalsndiFtw22ePCdPyWRK9jF5lh99+/ywmJBvp/sHz4/wwbMnzyeTF4dHz6cDpadulTvZOjJIiaWiuTHN7tpVrenFCAUhR/NtKp09Uyuy6UJe5weA/Drb0hC6GoOh2Nf+KvlCAtdb8Gg4h+5W4YOWfu4kipa4XbNP/bttbxYTpOHWLPKdmeBF2xdw7IiQtU3soiFOSlN70YKrSaOgUgk6afQwrpSToRfRgG3Yq+9zLpVEKl5ee0SMLdPZ9NyiTRkUu7QBz7qtpAdFePgUvQ53PtwCWJZNinfxHEavaqTqpNAZd+P3XKA/E6xkfxgqNdYKMsVNqaAWR+29RR6PmkzH0bjWEzJFjCM3ju/PuI02egMnYhN/XpBdeqPTAAM4n40tfGD60yaunohJ6vuNd8jYgaBHvYZbwoCdjPcY4phYRp2d8zXEohnGESK7xyTwyKqtJPy+tH0nYYLOvmwalLYxDT3JDrN1mwb+xYX+xaQTSirr0E/LHaEsF7/UIim2EdREmTbbscDSRh1OEU4RzwCeSD0nFRG43GJFoNdujp6Y0soX6BGdwk1OPlOperGGKJBX2i654FKQCOeCS4kEAa+7rarnyZoWY1Rw6A+c7mHwAh9Nn+7vT9sZPUGDo6Aj44bP1hNxzSfreIvMi6COGFvcXlSLtjvU+t6h0M9hXUQ3k2K/oFfDemn+k70a3Xthix6Nvr7xBbwZpsxR/6j+Z3gzUtD/Dt6MVWBs0Zthjtd/nDfDgG3dA2FJrQEq+hpcGsMw9+C992vc+zX6q7r3a9z7NdZF4r1f496vce/X2MSvEel8jShjhe/Thzer1btPH964G7YW/IoWxNSprUuiiP7VJDgimWs1eGSjd6ECLlbzG+phw92M7iqx2PTGIUXbYqgRUKXXBVGreayqJfSAd1zZmDvKEhUtR2H5tgIQWZncFmw6+mjkRQNCLDEGjQvnEGlf8pmlOv05lTYX7JdGqjZI0RUtbRHe0czCnjw+Bt1/7ofH4PtYYOmBHvmd7kpIQ+aGGM9h/w1rZMtyfnx09GTPGNv+9Ot3kfHtG8VrPfzAz2lquXXa7Cq1cOr3yujotNKqm8UhRGs20piqR4bNtAqwLwcQjThuRJnpMccjveEQGayiLRIk50wq0YAdjQvkNsqQZXzieyTa2ZAbbUEaz+aIbwvT5zB6p+HfyLdo2IGF7Awcw2OTNnk8dm2nahyowjDyMHY2U07vZrWvrIlmaLXxdqWWfcpMhpUmPX36HX+xYd7c6im2Pi00UTAx8OWyzUmPjanWbmRcJeCEgV4glrSjKu5A4zPu+6JZm05fLfKojlc0oM8mrSLDSQ5MkVnk51nTONLD99HRkyTQR0dPhjRvNd8WbZxB27AhyrDHtksSDjDIPNkWZPqQwQSWWXmhB2A1v5g87i780TB+LR3WkyJzONd/gnNNPkO96aAhQjgjhM+bY+Da6EUDMa7HAUr2xVGDtcDn/jcMc04a5d+KV6A6iDB2/bbHWlWrFi5Ygnkj9h2aETqOtMiTiyZELYjtmKAW3Jz2oXoLAs+qLbbw1Sco8P+AwDRVNqdk/M04IFLF68HN/CbJpB3wA2trJBHbzPX+ZMfv0O2g3U3Kzth3zAHM+MPQhHjpSPRywzwsvSkQv9B14aTr3MCrRuqFvvDkCgckpzhqRefM9XP1/SnBBwaacWg5108oMQkw7Y0EE82xNP0q1Bwz4xEoRq0mwqAU09JJ4cAfwL2I+LSFab5mNR4lmuuK8ZiQ7ehRYPKMnvdK9CTK+MQ+uK8h5Op9x6vRdEOwvGlf78/A+bibkB9cTkgkD6ySHuf6eneVF0o+a4WrFXBqMbxrs7pFivIJAIxeQ7u7SHa8hvM8lEbLsAV3pghfYVq2dQB6gJMK0+1px/rgwQxO3huAYo7l1oQgG/rnmMA8Dr8LWZMJFYAXofIaZ8sKun7pVxKX0CdJpk2psTwG0oASK8L+AwKlfDARNMwAysdlzA47Xa5yzPSFZq/xAXR1fQN3iq8fIP7GM2hqDAJwv2ahCSDqVexLwgNoUpNeLDORnEiJxXLg5okLjrX3Dwqfb3YLmSHdXdRGQ2hVx9bLcSUg3K2ov10ay4gfTs75wvZ5XpCJj8OAAKKgeL6pBYCFlr0aD3hUi+grNF5ZgK/ieJwWe0lVZuct/42WJd57mu2jR/Rszhn5n+jl2Sdk/o7en6ODw4sD05zRlT57jE7quiQ/k8lPVO0923+aHWQHT9Gjn378+PbNyLz7A8kv+WMXHrR3cJjto7d8Qkuyd/D09cHRC3SOp1jQvWf7vvbVmlfGTbiwmWw9XIaepHb/N2h7cTdb+pf+TnYhify12X4aiaYZUXZ3uDSksTkuLSD37Rzu2znct3O4b+ew+cLu2zn8397O4Rv0kVQ1FxiMbJ8hxpwo9DzbRwWW8wnHopCuwFPmPoE0nkYqNOPei5fLbFmBcw/qsCyoJEgRqSQqOHuoYIy4iPCEYBXeogZDuKQ+F6vGan5s7+ggnL+iM4ENFsCY0B+1001s9cidl5Ojf+PbhGoNxNZ7cr+8f/X+ONXn05pd90gu90y20d7B8xcRtEkIUqQysPfd1mZWnrGQnZMriJnui/wLIggSpOI+4Kq3oE91oZXAKS2JxukepXLPOkxxnnMoBuQqm/TVlazGykeabrCgM/1ZSugORbXEdBVlvnHbBtO91Z/dZDr8y42m05/dYDoj6W0+Xygt+tgIJzYOzMVlYnVBVOMmS0vLfwOT9nZwjUlT29ef1NJ1I0p/1MADv9YBOG8EzbHCqOJFYyogNhIM81kY+RoEf9zhee57piJ/5YNdPaxheg+8qP9n86/EFC+tzwZ6IHMG3/lMAGcNAwNPaYs42fZ1D2J1PGK2ilbkt1aB6TPbLkcNWbAxYXeGWMngDRzRZHzyC8mdRG/+cbEB0j1W4CS6/q2ACpfoEEFAhOhQaqg7DEzyWn/U0ZqgoFdRUFsxTetQkHphU/JgHp9lMdQ5tJPndpPkGgDNZIZZgsJ19SCIHYayX+jk7G2arJwDwhCV4ugKC8obCY1i21pgKQqyJSK56EsCK/buzH0Visv9IcOw4w3GtAkz0aCQAaJwVQ/u+9D+NLK3O50YcYiZPEYHa+rfDhLHI8w5pQxVNBdckpyzQiJJWU7QJ0Y/I1LzfN5Zj61+tikVn7AlwmJCFUiMdpCmY7dtawV68EYox7WCpsg2axb4/gjx2hB/uXT+Jv9G61JAqFgyXNE8Ngr0NHS/WXjWR3rEJFDMKC5qLHAl+3aFQVaSfGXCeUkwW/WKzHFJiotpybHqSPD6J8pmF1OcKy6O0cE+/AmpwmEh8qGtLj2JpiVWqMJ1bQMGmjDUU2PKhhaaiAJTkTRhU9LybQ+nG9hMpMKqkf1Y2+SJuEbXgYIPMJ7Rme1B0CDawo4yZRazl+ltVtGGdq4a4BrwT4LQTlEmQjt7kZ2DcEXLw4vOL6u0zmuhdIgWeDFCjTOHf/rwpiebkGzm1R+rzGr955rwt15AbsC/oYLnNhbjxk6tIbPhb8c7g6D1TEt3BJYed+QKYiwNb9zFUpJqUn4ZpCf6b9/Z6uZDQq1BegD9MOY7QWt3BludClgDuCBabQgarObbwladCkwzmGqj0gbgCgJ47xgqWxdi/Vg0C3I/+GyIAGM3950BDm7tGDYTUG/gMzFUCU6vz9ZFykx5Q1v+R3dBdbRVB1NJsBGZuIkIDYXQGLKKqDm/E/+Ch8kMuSlI7b1qrtyvSTyAZSVkBAfrCJFZhh4e7u8/TFvbKaNynrThpqS8a+57+wmirKC5sU20UektUGCUcDP7BIBIbu8UzVpXbj+PymNdp1J2WNtNIvqryrTYcKFNLFHbh1TUtJcIqSugFlAoujJXiSekvFCkqkusyDHa+de/QGb9979DYzmvCbsoKbu8oOzC9qi4UHjSE50bEQzWEdwryhwHOEY7T7P9bH+ns7Uw9THaybI9XNd7l3SCGf5mz9vO944OJk+Lbw/3d5+/ODzYPTggz3df5EfPd589nbw4ejp5mk8nT/50gb97BLz02PznwrDU40eY4XL5G7lY0LLIsSiO/x81Mi8+DMvqHP/h8NCj4Q+Hhw8fP368s3Ixz/RidnFZz/HB1tdUYjZr8Iwcl01OGEmu4B/tPv5j52EMPr8iYiGo3u3WHIRWepNuHx6wJpFeCxthV1RwVqVczhvXDgaA2gETvNjh+ja8uCfZ3EEGsW3bBDFBDkYbxpWA4A7v3c62rgOHv9capmiCE/7OqLRg/R7YG5g6iI2vyIKLy68NZR6w3wNpycm9qaptNzF8p68yfnXXQe8g+yQAyjdPsrOudyf/hZIFOq8xk9u9lOFO3vT6ekKeFOT5/v7u84Lsm+trcnDwdLeYfpt/u1/gw2J6cKMrOWwdQouNL+XwTt72mgau5M4KPPjRhexFUVDb++TWl49XkVs7oy2AiAVBD+3YD6Eck8s5AK9U6KZq29MNjMO4cmCakfQDyJGQmjhN0A3YsPvHMifo6ziRcCRPXzl2YkBTPChhazwAE6LVJIkU7zpETDPCr3I1FraUy2W9PstbUF7edQMqgnhz8+d3kUg3ACW06m2qKr63HhljiU4rjL31QoczRXLV3DY1RpNHOJo37mtoWmI37vuciwIKnW60OQlb5w0B7Vo27xBI2ve2bFBlLYYHK9eIK0gb2wSY/4iA4tWSZjjjyjje1VbHa2yOYMB1QbobE8XgXnRc25se6dMAl7VpieXtem0lZNd40jTbSxccveZy+MJtbaPfb9PS9toDUCeuvfVLBr5roDKWx3B7/TkrQ68Z9jow3Q1QPWAe+vbFN4Gq2xz3FpaOut8KdwUQnlfFyQDrB2qUVCqiT8LtoD+x1RtU1H3QD458zb5UdvF1Yk5SyEkDOQiiSwILL66Sz2amAFIYdTG43QlhcEMgTnvdBm4IQpykuSEUryH/8iYA+DiaMFV57e0LbvLe1T6MsrMoYrRl0zYAqL1WRkFhKwypeTabL4vIwAQ084m+D8KCD3/d/d41d9V/Q3OCCyJGevZW55hSIZVNM7cdPFsAfb2nsFdYMC2olWjOF0Qfj4rO5grZ1E6XDUnLpUa3VHo5pmiSreEw5WJmooQwqnBJcwg122DP1k6q/P9DFmUXEyvpspdVueF52iRr8ivNmVzBcpBJ2L8YjMRsA+pa3HUeriynO1ULKJ8HG2sbvlJN83NcTk0pPo1J47uMDjba00jJeTWJatkOLMcGeeYlb4o2zPOl/qeLwoPy/1gLgOmQz7f2V0OaefSpRLgI4vhwUVzACxduyCBE80GHFlu+ypsiqwX/heSq7Rvvicb+svt59VHtuCD0JxqjP3A+K4lZsQ8PPykpti1myiIMMw4c7zjzgMFSo2PXTzBJvrwyujmYw7XzaMvqr57Gt5jx72880xo5M525rkucScxmu6xcBIHLqyezH6ydAhTMZa2FtKRqebEySD2ccOirdWe1lLbuxvWofN15TA3TteaIXu2Ob/lBwfNLoFLLEF65fycOl/kNIiy6/Sjsb/poyzkX6sJE1LfmRczyORduvl3PDAZCxD1YaCNx3hoSoPZEjyuHaApQlf4kuR0DU1Up3+u1s+mvugnlG8yaNGZdN+nNpwN3j2wTZX7kC61tVxhKY0jypx4sq2KvN44swTNiPE7SpytYO6el2x/NvxKDnLIpD6mVi9ZC47hMQKD6eYo80b/+7Wa+bCZEMGJK2tv5fwqfJaBof/eXbHxjtoOicPbVp6n96NoTFQG92amqeZEmt402McBAzQujzSanam7rbghmOuMF+nT6Ku0xkDXO725R7Yj9yXjRO+q3nMyljaeiQfQxuf44rjeRPfcVrhMOAMa4beR/V9MFQ6bnvIYB3hSfftgBpF7H7W8/rxm3TcXadRlnlsGcnL01SVtd/gIPd303BiOgc2Ezt1Ks4IqShU00ezC8CDPFMdopeP73KHDr4T8zSX8jjx6j/4X20Z9Q6gWj7R8nf1v7Y9aUpdPQvPca1ZiysrWI98MT0qEJgCcfm7BWXEI6JuGaeITr/fbPp8+mON/ffZ4/w8Zvj/HTp7tPJvvF08P8+UH+bH/L4YHrRSHc+UpuFhQY0S6QLaIFonlHCl5FvfBVBncdZbOLS7JsyfC774AONa05ek2+DkDdBTXuppY0eCkP8JFXUD0/yK/DzNoQfPlxyDS1fVgQzzWRW+MCdsXU206UN0u6W+umtqmOa3bviUMFzE6ssmt0Qcqbshb0WpuMBWrQXfm964OW47K0JtAFltaoQCsslqgmoiZKYMXbvnRD8fIhMd3u9vjBjvQTWXas2OZsaN7fSMjodpPeNJrLJAm/Mg3nvsKorqfT/Rf4xfO756R9BvBFI7s2XdcAX02sIo7u6lEp+ZyTWqWCWLou9lXaWtqLb83MpVZ3BF+w/tlO5jq2EljRTb1a22f/0XESkxYiiUKLua39bJ7PcV0TRgqbIqSFqAmW4VdZGqyKSBkLhGiI5aA020lC650pBgA7yxAMUKTiNsixZS68CtPz6di9Sk7fKUZw3eT+OkgONsesKEn3m+EknPVQempycLiIUnAMbiHWATezuYKy/8bF5vodQxaRy8npq1q87zfe4KCcxGUg/JlxZgHN1n1G/UaHBYIwbkMQklwRaPDjU6hyLrqdKQM0zIjot6jacM6eb1r47tb9EO6tn8C2Rke5jC/Y1YcRqgpcrALqRkFGJ2ZgogRU4HUwoNeaXT00bZYYV1qhZCSHHip/kA+7gUY+IbomQi3dKNBZW9GyBJclFeB9ZUXQcIX82uDSxQ5FKzRdcyGbvS5xTua8BG+QIPDPog+Ba68hqWqwi93tjKohivtfG3UVKT6D8+utgNLFoVk1eUfrySY4zXWj3Elb5OxLNl/QNr62TsSXZ59se+2KiyVqzEp9iHHH9j0UpBbb7qxklFLIZRhMt7YKMDaf+aLK0jbEsrXg7IoGwjj9tVo3D2LyjBnYIFmO87rpTa3xBu6BdtsHHadc4TJjXFRZnffF9YGaGU56rYnI4+yqa1QK+4GmLT4FOCGpRdYQRb10zkKIPTJlVIANYan6oZNRQAN1UQBwLu1IpoeZmynngph27VQhAc2no9GAiPY1rR/s7/8h626RIcIb7pL5uLdRlrA32aveFnXCztzWdHu9rtoYPa6FpV8aGOeqSUy7yQULI7SnmBSwC1NBSOoqbZ+0yU1krdTkobVflyRs4NOzOCApM4Nk6BTCe3Jc5rZBtxZIC8SNZPL+PEPvGXpDWfMZ4mQ4k9ApxScg+TE7k9Zlo4fN55YmJ810SoSE4d6f/9U0hkYYyQaiWUPg9Ot6cMpwDqGVduv0pz+b0p8j+z3cGN3rjzueldkP9eDjHsHHEa+bUrz9OiB5d66d42UEp9Jz/IDRd3jmCkdjwDZvQJjrMs9VtDnIQK9hoauY6BpJ9nfLSO+alfaZaRdtvTOxxua9hW9aG7veJQoFLjQ6UiGztSBT+vkY7fwd0P/PnbW2VNLftsluIM0PWO4VFSFnDPdsjqOF+LxYKbP+dHcP3wcioS8EOicKndPfiOnyhCsttmsqSIDM87ypqQnjhD5r9p1HH07ePg7L2+2arjAVrmO/ynnwOILQ/wAlUSUiTNB8Dollah5GSxkbjtlTM8mFCUr3++xa0lS4zkIwkvJg8DsaMsqu77+N60ekv71mc9K1JFKTolVpvbfOs2CJkhJgd/Uoi1LiUkClkn5vBZfDja9U3tuAScOKEiiCdNrb3cD++8a1W3do8CuHFi76WraKmp7PRaGC5V/Fh6EGW0p4DsyT+AjUmKW9i0PlHcG9CLmWji7X9NDZ5OH4KNT4hknSvV2Yk88Xww4LtL4uEWRP1Ji5bqRY6hkQYTkvSNGvIr5hruJmBSJ/MLXD9St0xjx9YAOhtWbJnNfEPA6TfhP5HOuVF90MxJ8snvkUeXKyXc5yb3Br01Fsy8xHZHaMHhaTrOZSzQSRv5YZmLcfjtBDR0AZERP9b5BqH44QUfnjxLJkM9nKyk7QtBFgVZTNZLegVzSMg9ZzoEdgVm7XMEJRiazHieCKPGmHvy2sYNxz6L+kDPbDJqlRNXfb0Ex2Ae6wFbIVyICizHpM/0Znc0osQios1q+9GMLZxu0nGXVP5Eji4Rpc6D/vp1NJVI91BufjoWwLwbpubUtnloUFhtxg1C3KmqDDohHdpnZrV8z4cph5ZaHcdHVyyXLUW9pGBQKsuZ7IyFwPhLfAEpHPJG80Reqp5oIz3sgSGh/i6ElbK1tfd3HVjeDW+xj9EBuC2582irC58wofyUujY91OJyqEo6TFm5v4oIeum7CKSP/WkZTNylZwewTl2H54/RHtNZIIuXdMi4cpxr0m197G5ZOhh1aC0hfMBOeXeg9Z8Quf2IvmFuc7BPjuj/114jRogkV0yqkMd3Cd0y6IbMpbVrn6aMrhNaUK6l148QT9+PHjWVTWT589/XDX9CwswtdTMFZYXG5WGzq5gJsUYo4KLwcFmU1bQNBnNXAG0WVJhxEN72V/zP644UIGrECbFp5O8Pkaswu4Wm51wReC1/WAw7dfYzk9Flphq45HTBwTlPL+GTNJa3WwUNoSMiaGPD4s2YP/LwAA//90ULMQ" + return "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" } diff --git a/model/error/_meta/fields.yml b/model/error/_meta/fields.yml index e347cc66987..b5e45a86061 100644 --- a/model/error/_meta/fields.yml +++ b/model/error/_meta/fields.yml @@ -4,16 +4,16 @@ fields: - name: view errors type: keyword - script: "doc['service.name'].size() > 0 ? doc['service.name'].value : doc['service.name'].size() > 0 ? doc['service.name'].value : null" + script: "doc['service.name'].size() > 0 ? doc['service.name'].value : doc['context.service.name'].size() > 0 ? doc['context.service.name'].value : null" language: painless format: url label_template: "View Errors" open_link_in_current_tab: true url_template: - min_version: 5.0.0 - value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(query_string:(analyze_wildcard:!t,query:'service.name:%22{{value}}%22')))" + value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(query_string:(analyze_wildcard:!t,query:'context.service.name:%22{{value}}%22')))" - min_version: 6.0.0-alpha1 - value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(language:lucene,query:'service.name:\"{{value}}\"'))" + value: "../app/kibana#/dashboard/37f6fac0-7c6a-11e7-aa55-3b0d52c71c60?_a=(query:(language:lucene,query:'context.service.name:\"{{value}}\"'))" - name: error id icon type: keyword From 177980a8803380ec55ed911c46c93b6611fd5dcf Mon Sep 17 00:00:00 2001 From: simitt Date: Tue, 15 Jan 2019 09:08:38 +0100 Subject: [PATCH 6/8] PR comments --- model/error/event.go | 3 +-- model/metadata/user.go | 2 -- model/transaction/event.go | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/model/error/event.go b/model/error/event.go index 1df45fd9eac..3d22636b085 100644 --- a/model/error/event.go +++ b/model/error/event.go @@ -28,12 +28,11 @@ import ( "strconv" "time" - "github.com/elastic/apm-server/model/metadata" - "github.com/santhosh-tekuri/jsonschema" m "github.com/elastic/apm-server/model" "github.com/elastic/apm-server/model/error/generated/schema" + "github.com/elastic/apm-server/model/metadata" "github.com/elastic/apm-server/transform" "github.com/elastic/apm-server/utility" "github.com/elastic/apm-server/validation" diff --git a/model/metadata/user.go b/model/metadata/user.go index 3035dd0045f..5bb57e97679 100644 --- a/model/metadata/user.go +++ b/model/metadata/user.go @@ -20,7 +20,6 @@ package metadata import ( "encoding/json" "errors" - "fmt" "github.com/elastic/apm-server/utility" "github.com/elastic/beats/libbeat/common" @@ -52,7 +51,6 @@ func DecodeUser(input interface{}, err error) (*User, error) { //id can be string or int tmp := decoder.Interface(raw, "id") - fmt.Println(tmp) if tmp != nil { if t, ok := tmp.(json.Number); ok { id := t.String() diff --git a/model/transaction/event.go b/model/transaction/event.go index bcb92c2aaeb..bb4e078ba7e 100644 --- a/model/transaction/event.go +++ b/model/transaction/event.go @@ -21,10 +21,9 @@ import ( "errors" "time" - "github.com/elastic/apm-server/model/metadata" - "github.com/santhosh-tekuri/jsonschema" + "github.com/elastic/apm-server/model/metadata" "github.com/elastic/apm-server/model/transaction/generated/schema" "github.com/elastic/apm-server/transform" "github.com/elastic/apm-server/utility" From 75e636c555b1cea8c208689ac4a47cda3aa0f929 Mon Sep 17 00:00:00 2001 From: simitt Date: Thu, 17 Jan 2019 11:33:52 +0100 Subject: [PATCH 7/8] Update tests to reflect changes. --- ...TestPublishIntegrationErrors.approved.json | 16 ++++++++++- ...PublishIntegrationMetricsets.approved.json | 12 +++++++- ...blishIntegrationTransactions.approved.json | 9 ++++-- .../testIntakeIntegrationErrors.approved.json | 16 ++++++++++- ...tIntakeIntegrationMetricsets.approved.json | 12 +++++++- ...ntakeIntegrationTransactions.approved.json | 9 ++++-- testdata/intake-v2/errors.ndjson | 4 +-- testdata/intake-v2/metricsets.ndjson | 2 +- testdata/intake-v2/transactions.ndjson | 4 +-- testdata/intake-v2/transactions_spans.ndjson | 4 +-- tests/system/error.approved.json | 20 +++++++++++++ tests/system/transaction.approved.json | 28 ++++++++++++++----- 12 files changed, 112 insertions(+), 24 deletions(-) diff --git a/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json b/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json index 3a56f2f26de..98f3042f25e 100644 --- a/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json +++ b/beater/test_approved_es_documents/TestPublishIntegrationErrors.approved.json @@ -264,7 +264,6 @@ "us": 1494342245999999 }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -334,6 +333,11 @@ }, "timestamp": { "us": 1533826745999000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -401,6 +405,11 @@ }, "timestamp": { "us": 1547070053000000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -480,6 +489,11 @@ "id": "1234567890987654", "sampled": true, "type": "request" + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } } ] diff --git a/beater/test_approved_es_documents/TestPublishIntegrationMetricsets.approved.json b/beater/test_approved_es_documents/TestPublishIntegrationMetricsets.approved.json index 7080cd7b1bf..6438321b892 100644 --- a/beater/test_approved_es_documents/TestPublishIntegrationMetricsets.approved.json +++ b/beater/test_approved_es_documents/TestPublishIntegrationMetricsets.approved.json @@ -61,7 +61,12 @@ }, "name": "1234_service-12a3" }, - "short_counter": 227 + "short_counter": 227, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" + } }, { "@metadata": { @@ -102,6 +107,11 @@ "name": "ecmascript" }, "name": "1234_service-12a3" + }, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" } } ] diff --git a/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json b/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json index 487111bb67a..d0fd6e819f4 100644 --- a/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json +++ b/beater/test_approved_es_documents/TestPublishIntegrationTransactions.approved.json @@ -77,7 +77,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, { @@ -227,7 +229,6 @@ "type": "request" }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -324,7 +325,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } } ] diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json index 19d6f8a51ac..8384558a684 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationErrors.approved.json @@ -252,7 +252,6 @@ "us": 1494342245999999 }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -310,6 +309,11 @@ }, "timestamp": { "us": 1533826745999000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -365,6 +369,11 @@ }, "timestamp": { "us": 1533117600000000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -432,6 +441,11 @@ "id": "1234567890987654", "sampled": true, "type": "request" + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } } ] diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationMetricsets.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationMetricsets.approved.json index f8ff05fa38e..0ebf60f1c3b 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationMetricsets.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationMetricsets.approved.json @@ -49,7 +49,12 @@ }, "name": "1234_service-12a3" }, - "short_counter": 227 + "short_counter": 227, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" + } }, { "@timestamp": "2017-05-30T18:53:42.281Z", @@ -78,6 +83,11 @@ "name": "ecmascript" }, "name": "1234_service-12a3" + }, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" } } ] diff --git a/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json b/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json index 4b15cf17236..6048d4f379b 100644 --- a/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeIntegrationTransactions.approved.json @@ -65,7 +65,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, { @@ -203,7 +205,6 @@ "type": "request" }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -288,7 +289,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } } ] diff --git a/testdata/intake-v2/errors.ndjson b/testdata/intake-v2/errors.ndjson index 432ff8467ce..fc782bf3878 100644 --- a/testdata/intake-v2/errors.ndjson +++ b/testdata/intake-v2/errors.ndjson @@ -1,5 +1,5 @@ -{"metadata": {"process": {"ppid": 6789, "pid": 1234, "argv": ["node", "server.js"], "title": "node"}, "system": {"platform": "darwin", "hostname": "prod1.example.com", "architecture": "x64"}, "service": {"name": "1234_service-12a3", "language": {"version": "8", "name": "ecmascript"}, "agent": {"version": "3.14.0", "name": "elastic-node"}, "environment": "staging", "framework": {"version": "1.2.3", "name": "Express"}, "version": "5.1.3", "runtime": {"version": "8.0.0", "name": "node"}}}} -{"error": {"id": "0123456789012345", "timestamp": 1494342245999999, "culprit": "my.module.function_name","log": { "message": "My service could not talk to the database named foobar", "param_message": "My service could not talk to the database named %s", "logger_name": "my.logger.name", "level": "warning", "stacktrace": [ { "abs_path": "/real/file/name.py", "filename": "/webpack/file/name.py", "function": "foo", "vars": { "key": "value" }, "pre_context": ["line1", "line2"], "context_line": "line3","library_frame": false,"lineno": 3,"module": "App::MyModule","colno": 4,"post_context": ["line4","line5" ]},{"filename": "lib/instrumentation/index.js","lineno": 102,"function": "instrumented","abs_path": "/Users/watson/code/node_modules/elastic/lib/instrumentation/index.js","vars": {"key": "value"},"pre_context": [" var trans = this.currentTransaction",""," return instrumented",""," function instrumented () {"," var prev = ins.currentTransaction", " ins.currentTransaction = trans"],"context_line": " var result = original.apply(this, arguments)","post_context": [" ins.currentTransaction = prev"," return result","}","}","","Instrumentation.prototype._recoverTransaction = function (trans) {"," if (this.currentTransaction === trans) return"]}]},"exception": {"message": "The username root is unknown","type": "DbError","module": "__builtins__","code": 42,"handled": false,"attributes": {"foo": "bar" },"stacktrace": [{ "abs_path": "/real/file/name.py","filename": "file/name.py","function": "foo","vars": {"key": "value"},"pre_context": ["line1","line2"],"context_line": "line3", "library_frame": true,"lineno": 3,"module": "App::MyModule","colno": 4,"post_context": ["line4","line5"]},{"filename": "lib/instrumentation/index.js","lineno": 102,"function": "instrumented","abs_path": "/Users/watson/code/node_modules/elastic/lib/instrumentation/index.js","vars": {"key": "value"},"pre_context": [" var trans = this.currentTransaction",""," return instrumented",""," function instrumented () {", " var prev = ins.currentTransaction"," ins.currentTransaction = trans"],"context_line": " var result = original.apply(this, arguments)","post_context": [" ins.currentTransaction = prev"," return result","}","}","","Instrumentation.prototype._recoverTransaction = function (trans) {"," if (this.currentTransaction === trans) return"]}]},"context": {"request": {"socket": {"remote_address": "12.53.12.1","encrypted": true},"http_version": "1.1","method": "POST","url": {"protocol": "https:","full": "https://www.example.com/p/a/t/h?query=string#hash","hostname": "www.example.com","port": "8080","pathname": "/p/a/t/h","search": "?query=string", "hash": "#hash","raw": "/p/a/t/h?query=string#hash"},"headers": {"user-agent": "Mozilla Chrome Edge","content-type": "text/html","cookie": "c1=v1; c2=v2","some-other-header": "foo","array": ["foo","bar","baz"]}, "cookies": {"c1": "v1", "c2": "v2" },"env": {"SERVER_SOFTWARE": "nginx", "GATEWAY_INTERFACE": "CGI/1.1"},"body": "Hello World"},"response": { "status_code": 200, "headers": { "content-type": "application/json" },"headers_sent": true, "finished": true }, "user": { "id": 99, "username": "foo", "email": "foo@example.com"},"tags": {"organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8"}, "custom": {"my_key": 1,"some_other_value": "foo bar","and_objects": {"foo": ["bar","baz" ] }}}}} +{"metadata": {"process": {"ppid": 6789, "pid": 1234, "argv": ["node", "server.js"], "title": "node"}, "user": { "id": 123, "username": "bar", "email": "bar@example.com"}, "system": {"platform": "darwin", "hostname": "prod1.example.com", "architecture": "x64"}, "service": {"name": "1234_service-12a3", "language": {"version": "8", "name": "ecmascript"}, "agent": {"version": "3.14.0", "name": "elastic-node"}, "environment": "staging", "framework": {"version": "1.2.3", "name": "Express"}, "version": "5.1.3", "runtime": {"version": "8.0.0", "name": "node"}}}} +{"error": {"id": "0123456789012345", "timestamp": 1494342245999999, "culprit": "my.module.function_name","log": { "message": "My service could not talk to the database named foobar", "param_message": "My service could not talk to the database named %s", "logger_name": "my.logger.name", "level": "warning", "stacktrace": [ { "abs_path": "/real/file/name.py", "filename": "/webpack/file/name.py", "function": "foo", "vars": { "key": "value" }, "pre_context": ["line1", "line2"], "context_line": "line3","library_frame": false,"lineno": 3,"module": "App::MyModule","colno": 4,"post_context": ["line4","line5" ]},{"filename": "lib/instrumentation/index.js","lineno": 102,"function": "instrumented","abs_path": "/Users/watson/code/node_modules/elastic/lib/instrumentation/index.js","vars": {"key": "value"},"pre_context": [" var trans = this.currentTransaction",""," return instrumented",""," function instrumented () {"," var prev = ins.currentTransaction", " ins.currentTransaction = trans"],"context_line": " var result = original.apply(this, arguments)","post_context": [" ins.currentTransaction = prev"," return result","}","}","","Instrumentation.prototype._recoverTransaction = function (trans) {"," if (this.currentTransaction === trans) return"]}]},"exception": {"message": "The username root is unknown","type": "DbError","module": "__builtins__","code": 42,"handled": false,"attributes": {"foo": "bar" },"stacktrace": [{ "abs_path": "/real/file/name.py","filename": "file/name.py","function": "foo","vars": {"key": "value"},"pre_context": ["line1","line2"],"context_line": "line3", "library_frame": true,"lineno": 3,"module": "App::MyModule","colno": 4,"post_context": ["line4","line5"]},{"filename": "lib/instrumentation/index.js","lineno": 102,"function": "instrumented","abs_path": "/Users/watson/code/node_modules/elastic/lib/instrumentation/index.js","vars": {"key": "value"},"pre_context": [" var trans = this.currentTransaction",""," return instrumented",""," function instrumented () {", " var prev = ins.currentTransaction"," ins.currentTransaction = trans"],"context_line": " var result = original.apply(this, arguments)","post_context": [" ins.currentTransaction = prev"," return result","}","}","","Instrumentation.prototype._recoverTransaction = function (trans) {"," if (this.currentTransaction === trans) return"]}]},"context": {"request": {"socket": {"remote_address": "12.53.12.1","encrypted": true},"http_version": "1.1","method": "POST","url": {"protocol": "https:","full": "https://www.example.com/p/a/t/h?query=string#hash","hostname": "www.example.com","port": "8080","pathname": "/p/a/t/h","search": "?query=string", "hash": "#hash","raw": "/p/a/t/h?query=string#hash"},"headers": {"user-agent": "Mozilla Chrome Edge","content-type": "text/html","cookie": "c1=v1; c2=v2","some-other-header": "foo","array": ["foo","bar","baz"]}, "cookies": {"c1": "v1", "c2": "v2" },"env": {"SERVER_SOFTWARE": "nginx", "GATEWAY_INTERFACE": "CGI/1.1"},"body": "Hello World"},"response": { "status_code": 200, "headers": { "content-type": "application/json" },"headers_sent": true, "finished": true }, "user": { "id": 99, "username": "foo"},"tags": {"organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8"}, "custom": {"my_key": 1,"some_other_value": "foo bar","and_objects": {"foo": ["bar","baz" ] }}}}} { "error": {"id": "cdefab0123456789", "trace_id": null, "timestamp": 1533826745999000,"exception": {"message": "Cannot read property 'baz' no defined"}}} { "error": {"id": "cdefab0123456780", "trace_id": null, "exception": {"type": "DbError"}}} { "error": {"id": "abcdef0123456789", "trace_id": "0123456789abcdeffedcba0123456789", "parent_id": "9632587410abcdef", "transaction_id": "1234567890987654", "transaction": { "sampled": true, "type": "request"}, "timestamp": 1533827045999000,"log": {"level": "custom log level","message": "Cannot read property 'baz' of undefined"}}} diff --git a/testdata/intake-v2/metricsets.ndjson b/testdata/intake-v2/metricsets.ndjson index a6ebbc52f67..295fefec29c 100644 --- a/testdata/intake-v2/metricsets.ndjson +++ b/testdata/intake-v2/metricsets.ndjson @@ -1,3 +1,3 @@ -{"metadata": {"user": null, "process": {"ppid": null, "pid": 1234, "argv": null, "title": null}, "system": null, "service": {"name": "1234_service-12a3", "language": {"version": null, "name":"ecmascript"}, "agent": {"version": "3.14.0", "name": "elastic-node"}, "environment": null, "framework": null,"version": null, "runtime": null}}} +{"metadata": {"user": {"username": "logged-in-user", "id": "axb123hg", "email": "user@mail.com"}, "process": {"ppid": null, "pid": 1234, "argv": null, "title": null}, "system": null, "service": {"name": "1234_service-12a3", "language": {"version": null, "name":"ecmascript"}, "agent": {"version": "3.14.0", "name": "elastic-node"}, "environment": null, "framework": null,"version": null, "runtime": null}}} {"metricset": { "samples": { "byte_counter": { "value": 1 }, "short_counter": { "value": 227 }, "integer_gauge": { "value": 42767 }, "long_gauge": { "value": 3147483648 }, "float_gauge": { "value": 9.16 }, "double_gauge": { "value": 3.141592653589793 }, "dotted.float.gauge": { "value": 6.12 }, "negative.d.o.t.t.e.d": { "value": -1022 } }, "tags": { "some": "abc", "code": 200, "success": true }, "timestamp": 1496170422281000 }} { "metricset": { "samples": { "go.memstats.heap.sys.bytes": { "value": 6.520832e+06 } }, "timestamp": 1496170422281000 }} diff --git a/testdata/intake-v2/transactions.ndjson b/testdata/intake-v2/transactions.ndjson index 174e6dc3046..1c007463366 100644 --- a/testdata/intake-v2/transactions.ndjson +++ b/testdata/intake-v2/transactions.ndjson @@ -1,4 +1,4 @@ -{"metadata": {"service": {"name": "1234_service-12a3","version": "5.1.3","environment": "staging","language": {"name": "ecmascript","version": "8"},"runtime": {"name": "node","version": "8.0.0"},"framework": {"name": "Express","version": "1.2.3"},"agent": {"name": "elastic-node","version": "3.14.0"}},"user": {"id": "123user"}, "process": {"pid": 1234,"ppid": 6789,"title": "node","argv": ["node","server.js"]},"system": {"hostname": "prod1.example.com","architecture": "x64","platform": "darwin"}}} +{"metadata": {"service": {"name": "1234_service-12a3","version": "5.1.3","environment": "staging","language": {"name": "ecmascript","version": "8"},"runtime": {"name": "node","version": "8.0.0"},"framework": {"name": "Express","version": "1.2.3"},"agent": {"name": "elastic-node","version": "3.14.0"}},"user": {"id": "123user", "username": "bar", "email": "bar@user.com"}, "process": {"pid": 1234,"ppid": 6789,"title": "node","argv": ["node","server.js"]},"system": {"hostname": "prod1.example.com","architecture": "x64","platform": "darwin"}}} {"transaction": { "id": "945254c567a5417e", "trace_id": "0123456789abcdef0123456789abcdef", "parent_id": "abcdefabcdef01234567", "type": "request", "duration": 32.592981, "span_count": { "started": 43 }} } -{"transaction": {"id": "4340a8e0df1906ecbfa9", "trace_id": "0acd456789abcdef0123456789abcdef", "name": "GET /api/types","type": "request","duration": 32.592981,"result": "success", "timestamp": 1496170407154000, "sampled": true, "span_count": {"started": 17},"context": {"request": {"socket": {"remote_address": "12.53.12.1","encrypted": true},"http_version": "1.1","method": "POST","url": {"protocol": "https:","full": "https://www.example.com/p/a/t/h?query=string#hash","hostname": "www.example.com","port": "8080","pathname": "/p/a/t/h","search": "?query=string","hash": "#hash","raw": "/p/a/t/h?query=string#hash"},"headers": {"user-agent": "Mozilla Chrome Edge","content-type": "text/html","cookie": "c1=v1; c2=v2","some-other-header": "foo","array": ["foo","bar","baz"]},"cookies": {"c1": "v1","c2": "v2"},"env": {"SERVER_SOFTWARE": "nginx","GATEWAY_INTERFACE": "CGI/1.1"},"body": {"str": "hello world","additional": { "foo": {},"bar": 123,"req": "additional information"}}},"response": {"status_code": 200,"headers": {"content-type": "application/json"},"headers_sent": true,"finished": true}, "user": {"id": "99","username": "foo","email": "foo@example.com"},"tags": {"organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8", "tag2": 12, "tag3": 12.45, "tag4": false, "tag5": null },"custom": {"my_key": 1,"some_other_value": "foo bar","and_objects": {"foo": ["bar","baz"]},"(": "not a valid regex and that is fine"}}}} +{"transaction": {"id": "4340a8e0df1906ecbfa9", "trace_id": "0acd456789abcdef0123456789abcdef", "name": "GET /api/types","type": "request","duration": 32.592981,"result": "success", "timestamp": 1496170407154000, "sampled": true, "span_count": {"started": 17},"context": {"request": {"socket": {"remote_address": "12.53.12.1","encrypted": true},"http_version": "1.1","method": "POST","url": {"protocol": "https:","full": "https://www.example.com/p/a/t/h?query=string#hash","hostname": "www.example.com","port": "8080","pathname": "/p/a/t/h","search": "?query=string","hash": "#hash","raw": "/p/a/t/h?query=string#hash"},"headers": {"user-agent": "Mozilla Chrome Edge","content-type": "text/html","cookie": "c1=v1; c2=v2","some-other-header": "foo","array": ["foo","bar","baz"]},"cookies": {"c1": "v1","c2": "v2"},"env": {"SERVER_SOFTWARE": "nginx","GATEWAY_INTERFACE": "CGI/1.1"},"body": {"str": "hello world","additional": { "foo": {},"bar": 123,"req": "additional information"}}},"response": {"status_code": 200,"headers": {"content-type": "application/json"},"headers_sent": true,"finished": true}, "user": {"id": "99","username": "foo"},"tags": {"organization_uuid": "9f0e9d64-c185-4d21-a6f4-4673ed561ec8", "tag2": 12, "tag3": 12.45, "tag4": false, "tag5": null },"custom": {"my_key": 1,"some_other_value": "foo bar","and_objects": {"foo": ["bar","baz"]},"(": "not a valid regex and that is fine"}}}} {"transaction": { "id": "cdef4340a8e0df19", "trace_id": "0acd456789abcdef0123456789abcdef", "type": "request", "duration": 13.980558, "timestamp": 1532976822281000, "sampled": null, "span_count": { "dropped": 55, "started": 436 }, "marks": {"navigationTiming": {"appBeforeBootstrap": 608.9300000000001,"navigationStart": -21},"another_mark": {"some_long": 10,"some_float": 10.0}, "performance": {}}, "context": { "request": { "socket": { "remote_address": null, "encrypted": null }, "method": "POST", "headers": { "user-agent": null, "content-type": null, "cookie": null }, "url": { "protocol": null, "full": null, "hostname": null, "port": null, "pathname": null, "search": null, "hash": null, "raw": null } }, "response": { "headers": { "content-type": null } } }} } diff --git a/testdata/intake-v2/transactions_spans.ndjson b/testdata/intake-v2/transactions_spans.ndjson index 44c6ffa3e40..72cca922bc2 100644 --- a/testdata/intake-v2/transactions_spans.ndjson +++ b/testdata/intake-v2/transactions_spans.ndjson @@ -1,5 +1,5 @@ -{"metadata":{"service":{"name":"1234_service-12a3","version":"5.1.3","environment":"staging","language":{"name":"ecmascript","version":"8"},"runtime":{"name":"node","version":"8.0.0"},"framework":{"name":"Express","version":"1.2.3"},"agent":{"name":"elastic-node","version":"3.14.0"}},"process":{"pid":1234,"ppid":6789,"title":"node","argv":["node","server.js"]},"system":{"hostname":"prod1.example.com","architecture":"x64","platform":"darwin"}}} -{"transaction":{"id":"945254c567a5417e","name":"GET /api/types","type":"request","duration":32.592981,"result":"success","timestamp":1496170407154000,"sampled":true,"span_count":{"dropped":2,"started":4},"context":{"request":{"socket":{"remote_address":"12.53.12.1","encrypted":true},"http_version":"1.1","method":"POST","url":{"protocol":"https:","full":"https://www.example.com/p/a/t/h?query=string#hash","hostname":"www.example.com","port":"8080","pathname":"/p/a/t/h","search":"?query=string","hash":"#hash","raw":"/p/a/t/h?query=string#hash"},"headers":{"user-agent":"Mozilla Chrome Edge","content-type":"text/html","cookie":"c1=v1; c2=v2","some-other-header":"foo","array":["foo","bar","baz"]},"cookies":{"c1":"v1","c2":"v2"},"env":{"SERVER_SOFTWARE":"nginx","GATEWAY_INTERFACE":"CGI/1.1"},"body":{"str":"hello world","additional":{"foo":{},"bar":123,"req":"additional information"}}},"response":{"status_code":200,"headers":{"content-type":"application/json"},"headers_sent":true,"finished":true},"user":{"id":"99","username":"foo","email":"foo@example.com"},"tags":{"organization_uuid":"9f0e9d64-c185-4d21-a6f4-4673ed561ec8", "number_code": 2, "bool_error": false},"custom":{"my_key":1,"some_other_value":"foo bar","and_objects":{"foo":["bar","baz"]},"(":"not a valid regex and that is fine"}},"marks":{"navigationTiming":{"appBeforeBootstrap":608.9300000000001,"navigationStart":-21},"another_mark":{"some_long":10,"some_float":10.0},"performance":{}},"trace_id":"945254c567a5417eaaaaaaaaaaaaaaaa"}} +{"metadata":{"service":{"name":"1234_service-12a3","version":"5.1.3","environment":"staging","language":{"name":"ecmascript","version":"8"},"runtime":{"name":"node","version":"8.0.0"},"framework":{"name":"Express","version":"1.2.3"},"agent":{"name":"elastic-node","version":"3.14.0"}},"process":{"pid":1234,"ppid":6789,"title":"node","argv":["node","server.js"]},"system":{"hostname":"prod1.example.com","architecture":"x64","platform":"darwin"},"user": {"id": "123user", "username": "foo", "email": "foo@bar.com"}}} +{"transaction":{"id":"945254c567a5417e","name":"GET /api/types","type":"request","duration":32.592981,"result":"success","timestamp":1496170407154000,"sampled":true,"span_count":{"dropped":2,"started":4},"context":{"request":{"socket":{"remote_address":"12.53.12.1","encrypted":true},"http_version":"1.1","method":"POST","url":{"protocol":"https:","full":"https://www.example.com/p/a/t/h?query=string#hash","hostname":"www.example.com","port":"8080","pathname":"/p/a/t/h","search":"?query=string","hash":"#hash","raw":"/p/a/t/h?query=string#hash"},"headers":{"user-agent":"Mozilla Chrome Edge","content-type":"text/html","cookie":"c1=v1; c2=v2","some-other-header":"foo","array":["foo","bar","baz"]},"cookies":{"c1":"v1","c2":"v2"},"env":{"SERVER_SOFTWARE":"nginx","GATEWAY_INTERFACE":"CGI/1.1"},"body":{"str":"hello world","additional":{"foo":{},"bar":123,"req":"additional information"}}},"response":{"status_code":200,"headers":{"content-type":"application/json"},"headers_sent":true,"finished":true},"user":{"id":"99","email":"foo@example.com"},"tags":{"organization_uuid":"9f0e9d64-c185-4d21-a6f4-4673ed561ec8", "number_code": 2, "bool_error": false},"custom":{"my_key":1,"some_other_value":"foo bar","and_objects":{"foo":["bar","baz"]},"(":"not a valid regex and that is fine"}},"marks":{"navigationTiming":{"appBeforeBootstrap":608.9300000000001,"navigationStart":-21},"another_mark":{"some_long":10,"some_float":10.0},"performance":{}},"trace_id":"945254c567a5417eaaaaaaaaaaaaaaaa"}} {"span":{"id":"0aaaaaaaaaaaaaaa","timestamp":1496170407154000,"parent":null,"name":"SELECT FROM product_types","type":"db.postgresql.query","start":2.83092,"duration":3.781912,"sync":false,"stacktrace":[{"function":"onread","abs_path":"net.js","filename":"net.js","lineno":547,"library_frame":true,"vars":{"key":"value"},"module":"some module","colno":4,"context_line":"line3","pre_context":[" var trans = this.currentTransaction",""],"post_context":[" ins.currentTransaction = prev"," return result","}"]},{"filename":"my2file.js","lineno":10}],"context":{"db":{"instance":"customers","statement":"SELECT * FROM product_types WHERE user_id=?","type":"sql","user":"readonly_user"},"http":{"url":"http://localhost:8000","status_code":200,"method":"GET"},"tags":{"span_tag":"something"}},"transaction_id":"945254c567a5417e","parent_id":"945254c567a5417e","trace_id":"945254c567a5417eaaaaaaaaaaaaaaaa"}} {"span":{"id":"1aaaaaaaaaaaaaaa","timestamp":1496170407154000,"parent":0,"name":"GET /api/types","type":"request","start":0,"duration":32.592981,"transaction_id":"945254c567a5417e","parent_id":"945254c567a5417e","trace_id":"945254c567a5417eaaaaaaaaaaaaaaaa"}} {"span":{"id":"2aaaaaaaaaaaaaaa","timestamp":1496170407154000,"parent":1,"name":"GET /api/types","type":"request","start":1.845,"duration":3.5642981,"stacktrace":[],"context":{},"transaction_id":"945254c567a5417e","parent_id":"945254c567a5417e","trace_id":"945254c567a5417eaaaaaaaaaaaaaaaa"}} diff --git a/tests/system/error.approved.json b/tests/system/error.approved.json index a3d51874020..0097f8f25a9 100644 --- a/tests/system/error.approved.json +++ b/tests/system/error.approved.json @@ -56,6 +56,11 @@ }, "grouping_key": "18f82051862e494727fa20e0adc15711", "id": "7f0e9d68c1854d21a6f44673ed561ec8" + }, + "user": { + "username": "bar", + "id": 123, + "email": "bar@example.com" } }, "_index": "test-apm-12-12-2017" @@ -322,6 +327,11 @@ "processor": { "name": "error", "event": "error" + }, + "user": { + "username": "bar", + "id": 123, + "email": "bar@example.com" } }, "_index": "test-apm-12-12-2017" @@ -384,6 +394,11 @@ }, "grouping_key": "f6b5a2877d9b00d5b32b44c9db039f11", "id": "8f0e9d68c1854d21a6f44673ed561ec8" + }, + "user": { + "username": "bar", + "id": 123, + "email": "bar@example.com" } }, "_index": "test-apm-12-12-2017" @@ -446,6 +461,11 @@ "message": "Cannot read property 'baz' of undefined", "level": "custom log level" } + }, + "user": { + "username": "bar", + "id": 123, + "email": "bar@example.com" } }, "_index": "test-apm-12-12-2017" diff --git a/tests/system/transaction.approved.json b/tests/system/transaction.approved.json index cd99aebdbc3..f7db6d1c71c 100644 --- a/tests/system/transaction.approved.json +++ b/tests/system/transaction.approved.json @@ -6,17 +6,17 @@ "_source": { "@timestamp": "2017-05-30T18:53:27.154Z", "agent": { - "name": "elastic-node", + "name": "1elastic-node", "version": "3.14.0" }, "host": { "architecture": "x64", "hostname": "prod1.example.com", "platform": "darwin", - "ip": "127.0.0.1" + "ip": "1127.0.0.1" }, "observer": { - "name": "ed7e2cf02cd9", + "name": "123", "version": "7.0.0-alpha1", "hostname": "ed7e2cf02cd9" }, @@ -29,13 +29,13 @@ "node", "server.js" ], - "pid": 1234, + "pid": 11234, "title": "node", "ppid": 6789 }, "service": { "runtime": { - "name": "node", + "name": "1node", "version": "8.0.0" }, "framework": { @@ -51,8 +51,7 @@ "name": "1234_service-12a3" }, "user": { - "id": "99", - "username": "foo", + "id": "199", "email": "foo@example.com" }, "context": { @@ -220,6 +219,11 @@ "us": 13980 }, "result": "200" + }, + "user": { + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, "_index": "test-apm-12-12-2017" @@ -287,6 +291,11 @@ "us": 13980 }, "result": "200" + }, + "user": { + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, "_index": "test-apm-12-12-2017" @@ -354,6 +363,11 @@ }, "name": "GET /api/types", "result": "failure" + }, + "user": { + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, "_index": "test-apm-12-12-2017" From 7343c4fbe7545702f8254faf794acb01c0f658bd Mon Sep 17 00:00:00 2001 From: simitt Date: Thu, 17 Jan 2019 14:00:29 +0100 Subject: [PATCH 8/8] fix tests --- docs/data/elasticsearch/generated/errors.json | 16 +++++++++++++++- .../data/elasticsearch/generated/metricsets.json | 12 +++++++++++- .../elasticsearch/generated/transactions.json | 9 ++++++--- .../stream/package_tests/error_attrs_test.go | 3 ++- .../package_tests/transaction_attrs_test.go | 2 +- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/data/elasticsearch/generated/errors.json b/docs/data/elasticsearch/generated/errors.json index 19d6f8a51ac..8384558a684 100644 --- a/docs/data/elasticsearch/generated/errors.json +++ b/docs/data/elasticsearch/generated/errors.json @@ -252,7 +252,6 @@ "us": 1494342245999999 }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -310,6 +309,11 @@ }, "timestamp": { "us": 1533826745999000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -365,6 +369,11 @@ }, "timestamp": { "us": 1533117600000000 + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } }, { @@ -432,6 +441,11 @@ "id": "1234567890987654", "sampled": true, "type": "request" + }, + "user": { + "email": "bar@example.com", + "id": "123", + "name": "bar" } } ] diff --git a/docs/data/elasticsearch/generated/metricsets.json b/docs/data/elasticsearch/generated/metricsets.json index f8ff05fa38e..0ebf60f1c3b 100644 --- a/docs/data/elasticsearch/generated/metricsets.json +++ b/docs/data/elasticsearch/generated/metricsets.json @@ -49,7 +49,12 @@ }, "name": "1234_service-12a3" }, - "short_counter": 227 + "short_counter": 227, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" + } }, { "@timestamp": "2017-05-30T18:53:42.281Z", @@ -78,6 +83,11 @@ "name": "ecmascript" }, "name": "1234_service-12a3" + }, + "user": { + "email": "user@mail.com", + "id": "axb123hg", + "name": "logged-in-user" } } ] diff --git a/docs/data/elasticsearch/generated/transactions.json b/docs/data/elasticsearch/generated/transactions.json index 4b15cf17236..6048d4f379b 100644 --- a/docs/data/elasticsearch/generated/transactions.json +++ b/docs/data/elasticsearch/generated/transactions.json @@ -65,7 +65,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } }, { @@ -203,7 +205,6 @@ "type": "request" }, "user": { - "email": "foo@example.com", "id": "99", "name": "foo" } @@ -288,7 +289,9 @@ "type": "request" }, "user": { - "id": "123user" + "email": "bar@user.com", + "id": "123user", + "name": "bar" } } ] diff --git a/processor/stream/package_tests/error_attrs_test.go b/processor/stream/package_tests/error_attrs_test.go index 8840548d07a..a74a197d4e8 100644 --- a/processor/stream/package_tests/error_attrs_test.go +++ b/processor/stream/package_tests/error_attrs_test.go @@ -134,7 +134,8 @@ func TestErrorPayloadAttrsMatchFields(t *testing.T) { func TestErrorPayloadAttrsMatchJsonSchema(t *testing.T) { errorProcSetup().PayloadAttrsMatchJsonSchema(t, - errorPayloadAttrsNotInJsonSchema(), nil) + errorPayloadAttrsNotInJsonSchema(), + tests.NewSet("error.context.user.email")) } func TestErrorAttrsPresenceInError(t *testing.T) { diff --git a/processor/stream/package_tests/transaction_attrs_test.go b/processor/stream/package_tests/transaction_attrs_test.go index 49289e100d4..3f6371d7425 100644 --- a/processor/stream/package_tests/transaction_attrs_test.go +++ b/processor/stream/package_tests/transaction_attrs_test.go @@ -111,7 +111,7 @@ func TestTransactionPayloadMatchFields(t *testing.T) { func TestTransactionPayloadMatchJsonSchema(t *testing.T) { transactionProcSetup().PayloadAttrsMatchJsonSchema(t, transactionPayloadAttrsNotInJsonSchema(), - nil) + tests.NewSet("transaction.context.user.email")) } func TestAttrsPresenceInTransaction(t *testing.T) {