diff --git a/beater/api/profile/handler.go b/beater/api/profile/handler.go index 89eb85c5dbe..860b63d4952 100644 --- a/beater/api/profile/handler.go +++ b/beater/api/profile/handler.go @@ -134,7 +134,7 @@ func Handler( err: errors.Wrap(err, "invalid metadata"), } } - metadata, err := metadata.DecodeMetadata(raw) + metadata, err := metadata.DecodeMetadata(raw, false) if err != nil { return nil, requestError{ id: request.IDResponseErrorsDecode, diff --git a/docs/data/intake-api/generated/rum_v3_events.ndjson b/docs/data/intake-api/generated/rum_v3_events.ndjson index 87be0674a40..70a2d45f661 100644 --- a/docs/data/intake-api/generated/rum_v3_events.ndjson +++ b/docs/data/intake-api/generated/rum_v3_events.ndjson @@ -1,4 +1,14 @@ -{"metadata":{"process":{"pid":1234,"title":"/usr/lib/jvm/java-10-openjdk-amd64/bin/java","ppid":1,"argv":["-v"]},"system":{"architecture":"amd64","detected_hostname":"8ec7ceb99074","configured_hostname":"host1","platform":"Linux","container":{"id":"8ec7ceb990749e79b37f6dc6cd3628633618d6ce412553a552a0fa6b69419ad4"},"kubernetes":{"namespace":"default","pod":{"uid":"b17f231da0ad128dc6c6c0b2e82f6f303d3893e3","name":"instrumented-java-service"},"node":{"name":"node-name"}}},"service":{"name":"1234_service-12a3","version":"4.3.0","node":{"configured_name":"8ec7ceb990749e79b37f6dc6cd3628633618d6ce412553a552a0fa6b69419ad4"},"environment":"production","language":{"name":"Java","version":"10.0.2"},"agent":{"version":"1.10.0","name":"java","ephemeral_id":"e71be9ac-93b0-44b9-a997-5638f6ccfc36"},"framework":{"name":"spring","version":"5.0.0"},"runtime":{"name":"Java","version":"10.0.2"}},"labels":{"group":"experimental","ab_testing":true,"segment":5}}} -{"error":{"id":"9876543210abcdeffedcba0123456789","timestamp":1571657444929001,"trace_id":"0123456789abcdeffedcba0123456789","parent_id":"9632587410abcdef","transaction_id":"1234567890987654","transaction":{"sampled":true,"type":"request"},"culprit":"opbeans.controllers.DTInterceptor.preHandle(DTInterceptor.java:73)","log":{"message":"Request method 'POST' not supported","param_message":"Request method 'POST' /events/:event not supported","logger_name":"http404","level":"error","stacktrace":[{"abs_path":"/tmp/Socket.java","filename":"Socket.java","classname":"Request::Socket","function":"connect","vars":{"key":"value"},"pre_context":["line1","line2"],"context_line":"line3","library_frame":true,"lineno":3,"module":"java.net","colno":4,"post_context":["line4","line5"]},{"filename":"SimpleBufferingClientHttpRequest.java","lineno":102,"function":"executeInternal","abs_path":"/tmp/SimpleBufferingClientHttpRequest.java","vars":{"key":"value"}}]},"exception":{"message":"Theusernamerootisunknown","type":"java.net.UnknownHostException","handled":true,"module":"org.springframework.http.client","code":42,"handled":false,"attributes":{"foo":"bar"},"cause":[{"type":"InternalDbError","message":"something wrong writing a file","cause":[{"type":"VeryInternalDbError","message":"disk spinning way too fast"},{"type":"ConnectionError","message":"on top of it,internet doesn't work"}]}],"stacktrace":[{"abs_path":"/tmp/AbstractPlainSocketImpl.java","filename":"AbstractPlainSocketImpl.java","function":"connect","vars":{"key":"value"},"pre_context":["line1","line2"],"context_line":"3","library_frame":true,"lineno":3,"module":"java.net","colno":4,"post_context":["line4","line5"]},{"filename":"AbstractClientHttpRequest.java","lineno":102,"function":"execute","vars":{"key":"value"}}]},"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":{"Forwarded": "for=192.168.0.1", "host":"opbeans-java:3000","content-length":"0","cookie":["c1=v1","c2=v2"],"Elastic-Apm-Traceparent":"00-8c21b4b556467a0b17ae5da959b5f388-31301f1fb2998121-01"},"cookies":{"c1":"v1","c2":"v2"},"env":{"SERVER_SOFTWARE":"nginx","GATEWAY_INTERFACE":"CGI/1.1"},"body":"HelloWorld"},"response":{"status_code":200,"headers":{"content-type":"application/json"},"headers_sent":true,"finished":true},"user":{"id":99,"username":"foo","email":"user@foo.mail"},"tags":{"organization_uuid":"9f0e9d64-c185-4d21-a6f4-4673ed561ec8"},"custom":{"my_key":1,"some_other_value":"foobar","and_objects":{"foo":["bar","baz"]}},"service":{"name":"service1","node":{"configured_name":"node-xyz"},"language":{"version":"1.2"},"framework":{"version":"1","name":"Node"}}}}} -{"span":{"timestamp":1571657444929001,"type":"external","subtype":"http","id":"1234567890aaaade","transaction_id":"1234567890987654","trace_id":"abcdef0123456789abcdef9876543210","parent_id":"abcdef0123456789","action":"connect","sync":true,"name":"GET users-authenticated", "duration":3.781912,"stacktrace":[{"filename":"DispatcherServlet.java","lineno":547},{"function":"render","abs_path":"/tmp/AbstractView.java","filename":"AbstractView.java","lineno":547,"library_frame":true,"vars":{"key":"value"},"module":"org.springframework.web.servlet.view","colno":4,"context_line":"line3"}],"context":{"db":{"instance":"customers","statement":"SELECT * FROM product_types WHERE user_id = ?","type":"sql","user":"postgres","link":"other.db.com"},"http":{"url":"http://localhost:8000","status_code":200,"method":"GET"},"service":{"name":"opbeans-java-1","agent":{"version":"1.10.0-SNAPSHOT","name":"java","ephemeral_id":"e71be9ac-93b0-44b9-a997-5638f6ccfc36"}}}}} -{"transaction":{"timestamp":1571657444929001,"name":"ResourceHttpRequestHandler","type":"http","id":"4340a8e0df1906ecbfa9","trace_id":"0acd456789abcdef0123456789abcdef","parent_id":"abcdefabcdef01234567","span_count":{"started":17,"dropped":0},"duration":32.592981,"result":"HTTP2xx","sampled":true,"context":{"service":{"name":"experimental-java","agent":{"version":"1.10.0-SNAPSHOT","ephemeral_id":"e71be9ac-93b0-44b9-a997-5638f6ccfc36"}},"request":{"socket":{"remote_address":"12.53.12.1:8080","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/5.0(Macintosh;IntelMacOSX10_10_5)AppleWebKit/537.36(KHTML,likeGecko)Chrome/51.0.2704.103Safari/537.36","MozillaChromeEdge"],"content-type":"text/html","cookie":"c1=v1,c2=v2","Elastic-Apm-Traceparent":["00-33a0bd4cceff0370a7c57d807032688e-69feaabc5b88d7e8-01"]},"cookies":{"c1":"v1","c2":"v2"},"env":{"SERVER_SOFTWARE":"nginx","GATEWAY_INTERFACE":"CGI/1.1"},"body":{"string":"helloworld","additional":{"foo":{},"bar":123,"req":"additionalinformation"}}},"response":{"status_code":200,"headers":{"content-type":"application/json"},"headers_sent":true,"finished":true}, "user":{"id":"99","username":"foo","email":"foo@mail.com"},"tags":{"organization_uuid":"9f0e9d64-c185-4d21-a6f4-4673ed561ec8","tag5":null},"custom":{"my_key":1,"some_other_value":"foobar","and_objects":{"foo":["bar","baz"]},"(":"notavalidregexandthatisfine"}}}} +{"m":{"se":{"n":"apm-a-rum-test-e2e-general-usecase","ve":"0.0.1","a":{"n":"js-base","ve":"4.8.1"},"la":{"n":"javascript"}},"l":{"testTagKey":"testTagValue"}}} +{"x":{"id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"general-usecase-initial-p-load","t":"p-load","d":295,"c":{"p":{"rf":"h://localhost:8000/test/e2e/","url":"h://localhost:8000/test/e2e/general-usecase/"},"r":{"ts":983,"ebs":690,"dbs":690},"u":{"id":"uId","un":"un","em":"em"},"cu":{"testContext":"testContext"},"g":{"testTagKey":"testTagValue"}},"k":{"a":{"lp":131.03000004775822,"fb":5,"di":120,"dc":138,"fp":70.82500003930181},"nt":{"fs":0,"ls":0,"le":0,"cs":0,"ce":0,"qs":4,"rs":5,"re":6,"dl":14,"di":120,"ds":120,"de":122,"dc":138,"es":138,"ee":138}},"yc":{"sd":8},"sm":true}} +{"y":{"id":"bbd8bcc3be14d814","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"Requesting and receiving the document","t":"hard-navigation","su":"browser-timing","s":4,"d":2}} +{"y":{"id":"fc546e87a90a774f","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"Parsing the document, executing sy. scripts","t":"hard-navigation","su":"browser-timing","s":14,"d":106}} +{"y":{"id":"fb8f717930697299","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"h://localhost:8000/test/e2e/general-usecase/app.e2e-bundle.min.js","t":"rc","su":"script","s":22.53499999642372,"d":35.060000023804605,"c":{"h":{"url":"h://localhost:8000/test/e2e/general-usecase/app.e2e-bundle.min.js?token=REDACTED","r":{"ts":677175,"ebs":676864,"dbs":676864}},"dt":{"se":{"n":"h://localhost:8000","rc":"localhost:8000","t":"rc"},"ad":"localhost","po":8000}}}} +{"y":{"id":"9b80535c4403c9fb","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"OpenTracing y","t":"cu","s":96.92999999970198,"d":198.07000000029802}} +{"y":{"id":"5ecb8ee030749715","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"GET /test/e2e/common/data.json","t":"external","su":"h","sy":true,"s":98.94000005442649,"d":6.72499998472631,"c":{"h":{"mt":"GET","url":"h://localhost:8000/test/e2e/common/data.json?test=hamid","sc":200},"dt":{"se":{"n":"h://localhost:8000","rc":"localhost:8000","t":"external"},"ad":"localhost","po":8000}}}} +{"y":{"id":"27f45fd274f976d4","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"POST h://localhost:8003/data","t":"external","su":"h","sy":true,"s":106.52000003028661,"d":11.584999971091747,"c":{"h":{"mt":"POST","url":"h://localhost:8003/data","sc":200},"dt":{"se":{"n":"h://localhost:8003","rc":"localhost:8003","t":"external"},"ad":"localhost","po":8003}}}} +{"y":{"id":"a3c043330bc2015e","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"POST h://localhost:8003/fetch","t":"external","su":"h","sy":false,"s":119.93500008247793,"d":15.949999913573265,"c":{"h":{"mt":"POST","url":"h://localhost:8003/fetch","sc":200},"dt":{"se":{"n":"h://localhost:8003","rc":"localhost:8003","t":"external"},"ad":"localhost","po":8003}}}} +{"y":{"id":"bc7665dc25629379","x_id":"ec2e280be8345240","parent_id":"ec2e280be8345240","trace_id":"286ac3ad697892c406528f13c82e0ce1","n":"Fire \"DOMContentLoaded\" event","t":"hard-navigation","su":"browser-timing","s":120,"d":2}} +{"me":{"x":{"n":"general-usecase-initial-p-load","t":"p-load"},"sa":{"xdc":{"v":1},"xds":{"v":295},"xbc":{"v":1}}}} +{"me":{"x":{"n":"general-usecase-initial-p-load","t":"p-load"},"y":{"t":"Request"},"sa":{"ysc":{"v":1},"yss":{"v":1}}}} +{"me":{"x":{"n":"general-usecase-initial-p-load","t":"p-load"},"y":{"t":"Response"},"sa":{"ysc":{"v":1},"yss":{"v":1}}}} +{"me":{"x":{"n":"general-usecase-initial-p-load","t":"p-load"},"y":{"t":"Processing"},"sa":{"ysc":{"v":1},"yss":{"v":124}}}} \ No newline at end of file diff --git a/docs/spec/errors/rum_v3_error.json b/docs/spec/errors/rum_v3_error.json index 75a980572fe..269dd14b465 100644 --- a/docs/spec/errors/rum_v3_error.json +++ b/docs/spec/errors/rum_v3_error.json @@ -16,7 +16,7 @@ "type": ["string", "null"], "maxLength": 1024 }, - "transaction_id": { + "xid": { "type": ["string", "null"], "description": "Hex encoded 64 random bits ID of the correlated transaction. Must be present if trace_id and parent_id are set.", "maxLength": 1024 @@ -26,66 +26,66 @@ "type": ["string", "null"], "maxLength": 1024 }, - "transaction": { + "x": { "type": ["object", "null"], "description": "Data for correlating errors with transactions", "properties": { - "sampled": { + "sm": { "type": ["boolean", "null"], "description": "Transactions that are 'sampled' will include all available information. Transactions that are not sampled will not have 'spans' or 'context'. Defaults to true." }, - "type": { + "t": { "type": ["string", "null"], "description": "Keyword of specific relevance in the service's domain (eg: 'request', 'backgroundjob', etc)", "maxLength": 1024 } } }, - "context": { - "$ref": "./../context.json" + "c": { + "$ref": "./../rum_v3_context.json" }, - "culprit": { + "cu": { "description": "Function call which was the primary perpetrator of this event.", "type": ["string", "null"], "maxLength": 1024 }, - "exception": { + "ex": { "description": "Information about the originally thrown error.", "type": ["object", "null"], "properties": { - "code": { + "cd": { "type": ["string", "integer", "null"], "maxLength": 1024, "description": "The error code set when the error happened, e.g. database error code." }, - "message": { + "mg": { "description": "The original error message.", "type": ["string", "null"] }, - "module": { + "mo": { "description": "Describes the exception type's module namespace.", "type": ["string", "null"], "maxLength": 1024 }, - "attributes": { + "at": { "type": ["object", "null"] }, - "stacktrace": { + "st": { "type": ["array", "null"], "items": { - "$ref": "./../stacktrace_frame.json" + "$ref": "./../rum_v3_stacktrace_frame.json" }, "minItems": 0 }, - "type": { + "t": { "type": ["string", "null"], "maxLength": 1024 }, - "handled": { + "hd": { "type": ["boolean", "null"], "description": "Indicator whether the error was caught somewhere in the code or not." }, - "cause": { + "ca": { "type": ["array", "null"], "items": { "type": ["object", "null"], @@ -96,49 +96,49 @@ } }, "anyOf": [ - {"required": ["message"], "properties": {"message": {"type": "string"}}}, - {"required": ["type"], "properties": {"type": {"type": "string"}}} + {"required": ["mg"], "properties": {"mg": {"type": "string"}}}, + {"required": ["t"], "properties": {"t": {"type": "string"}}} ] }, "log": { "type": ["object", "null"], "description": "Additional information added when logging the error.", "properties": { - "level": { + "lv": { "description": "The severity of the record.", "type": ["string", "null"], "maxLength": 1024 }, - "logger_name": { + "ln": { "description": "The name of the logger instance used.", "type": ["string", "null"], "default": "default", "maxLength": 1024 }, - "message": { + "mg": { "description": "The additionally logged error message.", "type": "string" }, - "param_message": { + "pmg": { "description": "A parametrized message. E.g. 'Could not connect to %s'. The property message is still required, and should be equal to the param_message, but with placeholders replaced. In some situations the param_message is used to group errors together. The string is not interpreted, so feel free to use whichever placeholders makes sense in the client languange.", "type": ["string", "null"], "maxLength": 1024 }, - "stacktrace": { + "st": { "type": ["array", "null"], "items": { - "$ref": "./../stacktrace_frame.json" + "$ref": "./../rum_v3_stacktrace_frame.json" }, "minItems": 0 } }, - "required": ["message"] + "required": ["mg"] } }, "allOf": [ { "required": ["id"] }, - { "if": {"required": ["transaction_id"], "properties": {"transaction_id": { "type": "string" }}}, + { "if": {"required": ["xid"], "properties": {"xid": { "type": "string" }}}, "then": { "required": ["trace_id", "parent_id"], "properties": {"trace_id": { "type": "string" }, "parent_id": {"type": "string"}}}}, { "if": {"required": ["trace_id"], "properties": {"trace_id": { "type": "string" }}}, "then": { "required": ["parent_id"], "properties": {"parent_id": { "type": "string" }}} }, @@ -146,7 +146,7 @@ "then": { "required": ["trace_id"], "properties": {"trace_id": { "type": "string" }}} } ], "anyOf": [ - { "required": ["exception"], "properties": {"exception": { "type": "object" }} }, + { "required": ["ex"], "properties": {"ex": { "type": "object" }} }, { "required": ["log"], "properties": {"log": { "type": "object" }} } ] } diff --git a/docs/spec/rum_v3_context.json b/docs/spec/rum_v3_context.json index 4b6829537af..3f5c5c9d223 100644 --- a/docs/spec/rum_v3_context.json +++ b/docs/spec/rum_v3_context.json @@ -2,57 +2,74 @@ "$id": "doc/spec/rum_v3_context.json", "title": "Context", "description": "Any arbitrary contextual information regarding the event, captured by the agent, optionally provided by the user", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { "cu": { "description": "An arbitrary mapping of additional metadata to store with the event.", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "patternProperties": { "^[^.*\"]*$": {} }, "additionalProperties": false }, "r": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "allOf": [ { "properties": { - "finished": { - "description": "A boolean indicating whether the response was finished or not", - "type": [ - "boolean", - "null" - ] - }, - "headers_sent": { + "sc": { "type": [ - "boolean", + "integer", "null" - ] - }, - "sc": { - "type": ["integer", "null"], + ], "description": "The status code of the http request." }, "ts": { - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "description": "Total size of the payload." }, "ebs": { - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "description": "The encoded size of the payload." }, - "dbs": { - "type": ["number", "null"], + "dbs": { + "type": [ + "number", + "null" + ], "description": "The decoded size of the payload." }, - "headers": { - "type": ["object", "null"], + "he": { + "type": [ + "object", + "null" + ], "patternProperties": { "[.*]*$": { - "type": ["string", "array", "null"], + "type": [ + "string", + "array", + "null" + ], "items": { - "type": ["string"] + "type": [ + "string" + ] } } } @@ -63,236 +80,85 @@ }, "q": { "properties": { - "body": { - "description": "Data should only contain the request body (not the query string). It can either be a dictionary (for standard HTTP requests) or a raw request body.", - "type": ["object", "string", "null"] - }, - "env": { + "en": { "description": "The env variable is a compounded of environment information passed from the webserver.", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": {} }, - "headers": { + "he": { "description": "Should include any headers sent by the requester. Cookies will be taken by headers if supplied.", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "patternProperties": { "[.*]*$": { - "type": ["string", "array", "null"], + "type": [ + "string", + "array", + "null" + ], "items": { - "type": ["string"] + "type": [ + "string" + ] } } } }, - "http_version": { + "hve": { "description": "HTTP version.", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, "mt": { "description": "HTTP method.", "type": "string", "maxLength": 1024 - }, - "socket": { - "type": ["object", "null"], - "properties": { - "encrypted": { - "description": "Indicates whether request was sent as SSL/HTTPS request.", - "type": ["boolean", "null"] - }, - "remote_address": { - "description": "The network address sending the request. Should be obtained through standard APIs and not parsed from any headers like 'Forwarded'.", - "type": ["string", "null"] - } - } - }, - "url": { - "description": "A complete Url, with scheme, host and path.", - "type": "object", - "properties": { - "raw": { - "type": ["string", "null"], - "description": "The raw, unparsed URL of the HTTP request line, e.g https://example.com:443/search?q=elasticsearch. This URL may be absolute or relative. For more details, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2.", - "maxLength": 1024 - }, - "protocol": { - "type": ["string", "null"], - "description": "The protocol of the request, e.g. 'https:'.", - "maxLength": 1024 - }, - "full": { - "type": ["string", "null"], - "description": "The full, possibly agent-assembled URL of the request, e.g https://example.com:443/search?q=elasticsearch#top.", - "maxLength": 1024 - }, - "hostname": { - "type": ["string", "null"], - "description": "The hostname of the request, e.g. 'example.com'.", - "maxLength": 1024 - }, - "port": { - "type": ["string", "integer","null"], - "description": "The port of the request, e.g. '443'", - "maxLength": 1024 - }, - "pathname": { - "type": ["string", "null"], - "description": "The path of the request, e.g. '/search'", - "maxLength": 1024 - }, - "search": { - "description": "The search describes the query string of the request. It is expected to have values delimited by ampersands.", - "type": ["string", "null"], - "maxLength": 1024 - }, - "hash": { - "type": ["string", "null"], - "description": "The hash of the request URL, e.g. 'top'", - "maxLength": 1024 - } - } - }, - "cookies": { - "description": "A parsed key-value object of cookies", - "type": ["object", "null"] } }, - "required": ["url", "mt"] + "required": [ + "mt" + ] }, "g": { "$ref": "tags.json" }, "u": { - "description": "Describes the correlated user for this event. If user data are provided here, all user related information from metadata is ignored, otherwise the metadata's user information will be stored with the event.", - "properties": { - "id": { - "description": "Identifier of the logged in user, e.g. the primary key of the user", - "type": ["string", "integer", "null"], - "maxLength": 1024 - }, - "em": { - "description": "Email of the logged in user", - "type": ["string", "null"], - "maxLength": 1024 - }, - "un": { - "description": "The username of the logged in user", - "type": ["string", "null"], - "maxLength": 1024 - } - } + "$ref": "rum_v3_user.json" }, "p": { "description": "", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { "rf": { "description": "RUM specific field that stores the URL of the page that 'linked' to the current page.", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "url": { "description": "RUM specific field that stores the URL of the current page", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] } } }, "se": { "description": "Service related information can be sent per event. Provided information will override the more generic information from metadata, non provided fields will be set according to the metadata information.", - "properties": { - "a": { - "description": "Name and version of the Elastic APM agent", - "type": ["object", "null"], - "properties": { - "n": { - "description": "Name of the Elastic APM agent, e.g. \"Python\"", - "type": ["string", "null"], - "maxLength": 1024 - }, - "ve": { - "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", - "type": ["string", "null"], - "maxLength": 1024 - }, - "ephemeral_id": { - "description": "Free format ID used for metrics correlation by some agents", - "type": ["string", "null"], - "maxLength": 1024 - } - } - }, - "framework": { - "description": "Name and version of the web framework used", - "type": ["object", "null"], - "properties": { - "n": { - "type": ["string", "null"], - "maxLength": 1024 - }, - "ve": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - }, - "la": { - "description": "Name and version of the programming language used", - "type": ["object", "null"], - "properties": { - "name": { - "type": ["string", "null"], - "maxLength": 1024 - }, - "version": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - }, - "n": { - "description": "Immutable name of the service emitting this event", - "type": ["string", "null"], - "pattern": "^[a-zA-Z0-9 _-]+$", - "maxLength": 1024 - }, - "environment": { - "description": "Environment name of the service, e.g. \"production\" or \"staging\"", - "type": ["string", "null"], - "maxLength": 1024 - }, - "runtime": { - "description": "Name and version of the language runtime running this service", - "type": ["object", "null"], - "properties": { - "name": { - "type": ["string", "null"], - "maxLength": 1024 - }, - "version": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - }, - "ve": { - "description": "Version of the service emitting this event", - "type": ["string", "null"], - "maxLength": 1024 - }, - "node": { - "description": "Unique meaningful name of the service node.", - "type": ["object", "null"], - "properties": { - "configured_name": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - } - } - }, - "mg": { - "$ref": "message.json" + "$ref": "rum_v3_service.json" } } -} \ No newline at end of file +} diff --git a/docs/spec/rum_v3_metadata.json b/docs/spec/rum_v3_metadata.json new file mode 100644 index 00000000000..42186223ed6 --- /dev/null +++ b/docs/spec/rum_v3_metadata.json @@ -0,0 +1,45 @@ +{ + "$id": "doc/spec/rum_v3_metadata.json", + "title": "Metadata", + "description": "Metadata concerning the other objects in the stream.", + "type": [ + "object" + ], + "properties": { + "se": { + "$ref": "rum_v3_service.json", + "type": "object", + "required": [ + "n", + "a" + ], + "properties.n.type": "string", + "properties.a.type": "string", + "properties.a.required": [ + "n", + "ve" + ], + "properties.a.properties.n.type": "string", + "properties.a.properties.ve.type": "string", + "properties.ru.required": [ + "n", + "ve" + ], + "properties.ru.properties.n.type": "string", + "properties.ru.properties.ve.type": "string", + "properties.la.required": [ + "n" + ], + "properties.la.properties.n.type": "string" + }, + "u": { + "$ref": "rum_v3_user.json" + }, + "l": { + "$ref": "tags.json" + } + }, + "required": [ + "se" + ] +} diff --git a/docs/spec/rum_v3_service.json b/docs/spec/rum_v3_service.json new file mode 100644 index 00000000000..f14fb495739 --- /dev/null +++ b/docs/spec/rum_v3_service.json @@ -0,0 +1,129 @@ +{ + "$id": "doc/spec/rum_v3_service.json", + "title": "Service", + "type": [ + "object", + "null" + ], + "properties": { + "a": { + "description": "Name and version of the Elastic APM agent", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "description": "Name of the Elastic APM agent, e.g. \"Python\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "fw": { + "description": "Name and version of the web framework used", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "la": { + "description": "Name and version of the programming language used", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "n": { + "description": "Immutable name of the service emitting this event", + "type": [ + "string", + "null" + ], + "pattern": "^[a-zA-Z0-9 _-]+$", + "maxLength": 1024 + }, + "en": { + "description": "Environment name of the service, e.g. \"production\" or \"staging\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ru": { + "description": "Name and version of the language runtime running this service", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "ve": { + "description": "Version of the service emitting this event", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } +} \ No newline at end of file diff --git a/docs/spec/rum_v3_stacktrace_frame.json b/docs/spec/rum_v3_stacktrace_frame.json index f86acb63124..e11f9cb868b 100644 --- a/docs/spec/rum_v3_stacktrace_frame.json +++ b/docs/spec/rum_v3_stacktrace_frame.json @@ -6,61 +6,84 @@ "properties": { "ap": { "description": "The absolute path of the file involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "co": { "description": "Column number", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "context_line": { + "cli": { "description": "The line of code part of the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "f": { "description": "The relative filename of the code involved in the stack frame, used e.g. to do error checksumming", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "classname": { + "cn": { "description": "The classname of the code involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "fn": { "description": "The function involved in the stack frame", - "type": ["string", "null"] - }, - "library_frame": { - "description": "A boolean, indicating if this frame is from a library or user code", - "type": ["boolean", "null"] + "type": [ + "string", + "null" + ] }, "li": { "description": "The line number of code part of the stack frame, used e.g. to do error checksumming", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "module": { + "mo": { "description": "The module to which frame belongs to", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "post_context": { + "poc": { "description": "The lines of code after the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } }, - "pre_context": { + "prc": { "description": "The lines of code before the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } - }, - "vars": { - "description": "Local variables for this stack frame", - "type": ["object", "null"], - "properties": {} } }, - "required": ["f"] -} + "required": [ + "f" + ] +} \ No newline at end of file diff --git a/docs/spec/rum_v3_user.json b/docs/spec/rum_v3_user.json new file mode 100644 index 00000000000..cf71104f2d5 --- /dev/null +++ b/docs/spec/rum_v3_user.json @@ -0,0 +1,35 @@ +{ + "$id": "docs/spec/rum_v3_user.json", + "title": "User", + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "description": "Identifier of the logged in user, e.g. the primary key of the user", + "type": [ + "string", + "integer", + "null" + ], + "maxLength": 1024 + }, + "em": { + "description": "Email of the logged in user", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "un": { + "description": "The username of the logged in user", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } +} \ No newline at end of file diff --git a/docs/spec/spans/rum_v3_span.json b/docs/spec/spans/rum_v3_span.json index e1753021796..82fa12957b2 100644 --- a/docs/spec/spans/rum_v3_span.json +++ b/docs/spec/spans/rum_v3_span.json @@ -11,7 +11,10 @@ "maxLength": 1024 }, "transaction_id": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "Hex encoded 64 random bits ID of the correlated transaction.", "maxLength": 1024 }, @@ -26,7 +29,10 @@ "maxLength": 1024 }, "s": { - "type": ["number", "null"], + "type": [ + "number", + "null" + ], "description": "Offset relative to the transaction's timestamp identifying the start of the span, in milliseconds" }, "t": { @@ -35,101 +41,116 @@ "maxLength": 1024 }, "su": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "A further sub-division of the type (e.g. postgresql, elasticsearch)", "maxLength": 1024 }, - "action": { - "type": ["string", "null"], + "ac": { + "type": [ + "string", + "null" + ], "description": "The specific kind of event within the sub-type represented by the span (e.g. query, connect)", "maxLength": 1024 }, "c": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "description": "Any other arbitrary data captured by the agent, optionally provided by the user", "properties": { "dt": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "description": "An object containing contextual data about the destination for spans", "properties": { "ad": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "Destination network address: hostname (e.g. 'localhost'), FQDN (e.g. 'elastic.co'), IPv4 (e.g. '127.0.0.1') or IPv6 (e.g. '::1')", "maxLength": 1024 }, "po": { - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "description": "Destination network port (e.g. 443)" }, "se": { "description": "Destination service context", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { "t": { "description": "Type of the destination service (e.g. 'db', 'elasticsearch'). Should typically be the same as span.type.", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, "n": { "description": "Identifier for the destination service (e.g. 'http://elastic.co', 'elasticsearch', 'rabbitmq')", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, "rc": { "description": "Identifier for the destination service resource being operated on (e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name')", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } }, - "required": ["t", "n", "rc"] - } - } - }, - "db": { - "type": ["object", "null"], - "description": "An object containing contextual data for database spans", - "properties": { - "instance": { - "type": ["string", "null"], - "description": "Database instance name" - }, - "link": { - "type": ["string", "null"], - "maxLength": 1024, - "description": "Database link" - }, - "statement": { - "type": ["string", "null"], - "description": "A database statement (e.g. query) for the given database type" - }, - "t": { - "type": ["string", "null"], - "description": "Database type. For any SQL database, \"sql\". For others, the lower-case database category, e.g. \"cassandra\", \"hbase\", or \"redis\"" - }, - "u": { - "type": ["string", "null"], - "description": "Username for accessing database" - }, - "rows_affected": { - "type": ["integer", "null"], - "description": "Number of rows affected by the SQL statement (if applicable)" + "required": [ + "t", + "n", + "rc" + ] } } }, "h": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "description": "An object containing contextual data of the related http request.", "properties": { "url": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "The raw url of the correlating http request." }, "sc": { - "type": ["integer", "null"], + "type": [ + "integer", + "null" + ], "description": "The status code of the http request." }, "mt": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024, "description": "The method of the http request." } @@ -163,11 +184,6 @@ "null" ], "maxLength": 1024 - }, - "ephemeral_id": { - "description": "Free format ID used for metrics correlation by some agents", - "type": ["string", "null"], - "maxLength": 1024 } } }, @@ -181,9 +197,6 @@ "maxLength": 1024 } } - }, - "mg": { - "$ref": "../message.json" } } }, @@ -197,7 +210,10 @@ "maxLength": 1024 }, "st": { - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "description": "List of stack frames with variable attributes (eg: lineno, filename, etc)", "items": { "$ref": "../rum_v3_stacktrace_frame.json" @@ -205,12 +221,31 @@ "minItems": 0 }, "sy": { - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "description": "Indicates whether the span was executed synchronously or asynchronously." } }, - "required": ["d", "n", "t", "id","trace_id", "parent_id"] + "required": [ + "d", + "n", + "t", + "id", + "trace_id", + "parent_id" + ] }, - {"required": ["s"], "properties": {"s": { "type": "number" }}} + { + "required": [ + "s" + ], + "properties": { + "s": { + "type": "number" + } + } + } ] -} +} \ No newline at end of file diff --git a/docs/spec/transactions/rum_v3_transaction.json b/docs/spec/transactions/rum_v3_transaction.json index e40e8cafc97..886ff0acdec 100644 --- a/docs/spec/transactions/rum_v3_transaction.json +++ b/docs/spec/transactions/rum_v3_transaction.json @@ -17,7 +17,10 @@ }, "parent_id": { "description": "Hex encoded 64 random bits ID of the parent transaction or span. Only root transactions of a trace do not have a parent_id, otherwise it needs to be set.", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, "t": { @@ -26,38 +29,52 @@ "maxLength": 1024 }, "n": { - "type": ["string","null"], + "type": [ + "string", + "null" + ], "description": "Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')", "maxLength": 1024 - }, + }, "yc": { "type": "object", "properties": { "sd": { "type": "integer", - "description": "Number of correlated ys that are recorded." + "description": "Number of correlated spans that are recorded." }, "dd": { - "type": ["integer","null"], - "description": "Number of ys that have been dd by the a recording the x." + "type": [ + "integer", + "null" + ], + "description": "Number of spans that have been dd by the a recording the x." } }, - "required": ["sd"] + "required": [ + "sd" + ] }, "c": { - "$ref": "../context.json" + "$ref": "../rum_v3_context.json" }, "d": { "type": "number", "description": "How long the transaction took to complete, in ms with 3 decimal points" }, - "result": { - "type": ["string", "null"], + "rt": { + "type": [ + "string", + "null" + ], "description": "The result of the transaction. For HTTP-related transactions, this should be the status code formatted like 'HTTP 2xx'.", "maxLength": 1024 }, "k": { - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "description": "A mark captures the timing of a significant event during the lifetime of a transaction. Marks are organized into groups and can be set by the user or the agent.", "patternProperties": { "^[^.*\"]*$": { @@ -67,11 +84,20 @@ "additionalProperties": false }, "sm": { - "type": ["boolean", "null"], + "type": [ + "boolean", + "null" + ], "description": "Transactions that are 'sampled' will include all available information. Transactions that are not sampled will not have 'spans' or 'context'. Defaults to true." } }, - "required": ["id", "trace_id", "yc", "d", "t"] + "required": [ + "id", + "trace_id", + "yc", + "d", + "t" + ] } ] } diff --git a/model/config.go b/model/config.go index 44ea4eb3db0..2c58360f4d7 100644 --- a/model/config.go +++ b/model/config.go @@ -19,4 +19,6 @@ package model type Config struct { Experimental bool + // RUM v3 support + HasShortFieldNames bool } diff --git a/model/context.go b/model/context.go index 7d0af35efaa..3f213c4456d 100644 --- a/model/context.go +++ b/model/context.go @@ -24,6 +24,8 @@ import ( "strconv" "strings" + "github.com/elastic/apm-server/model/fields" + "github.com/elastic/apm-server/model/metadata" "github.com/elastic/beats/v7/libbeat/common" @@ -123,24 +125,26 @@ func DecodeContext(input interface{}, cfg Config, err error) (*Context, error) { } decoder := utility.ManualDecoder{} - ctxInp := decoder.MapStr(raw, "context") + field := fields.Mapper(cfg.HasShortFieldNames) + + ctxInp := decoder.MapStr(raw, field("context")) if ctxInp == nil { return &Context{}, decoder.Err } - userInp := decoder.Interface(ctxInp, "user") - serviceInp := decoder.Interface(ctxInp, "service") + userInp := decoder.Interface(ctxInp, field("user")) + serviceInp := decoder.Interface(ctxInp, field("service")) var experimental interface{} if cfg.Experimental { experimental = decoder.Interface(ctxInp, "experimental") } - http, err := decodeHttp(ctxInp, decoder.Err) + http, err := decodeHttp(ctxInp, cfg.HasShortFieldNames, decoder.Err) url, err := decodeUrl(ctxInp, err) - labels, err := decodeLabels(ctxInp, err) - custom, err := decodeCustom(ctxInp, err) - page, err := decodePage(ctxInp, err) - service, err := metadata.DecodeService(serviceInp, err) - user, err := metadata.DecodeUser(userInp, err) + labels, err := decodeLabels(ctxInp, cfg.HasShortFieldNames, err) + custom, err := decodeCustom(ctxInp, cfg.HasShortFieldNames, err) + page, err := decodePage(ctxInp, cfg.HasShortFieldNames, err) + service, err := metadata.DecodeService(serviceInp, cfg.HasShortFieldNames, err) + user, err := metadata.DecodeUser(userInp, cfg.HasShortFieldNames, err) user = addUserAgent(user, http) client, err := decodeClient(user, http, err) message, err := DecodeMessage(ctxInp, err) @@ -304,31 +308,33 @@ func decodeClient(user *metadata.User, http *Http, err error) (*Client, error) { return nil, nil } -func decodeHttp(raw common.MapStr, err error) (*Http, error) { +func decodeHttp(raw common.MapStr, hasShortFieldNames bool, err error) (*Http, error) { if err != nil { return nil, err } var h *Http decoder := utility.ManualDecoder{} - inpReq := decoder.MapStr(raw, "request") + field := fields.Mapper(hasShortFieldNames) + + inpReq := decoder.MapStr(raw, field("request")) if inpReq != nil { h = &Http{ - Version: decoder.StringPtr(inpReq, "http_version"), + Version: decoder.StringPtr(inpReq, field("http_version")), Request: &Req{ - Method: strings.ToLower(decoder.String(inpReq, "method")), - Env: decoder.Interface(inpReq, "env"), + Method: strings.ToLower(decoder.String(inpReq, field("method"))), + Env: decoder.Interface(inpReq, field("env")), Socket: &Socket{ RemoteAddress: decoder.StringPtr(inpReq, "remote_address", "socket"), Encrypted: decoder.BoolPtr(inpReq, "encrypted", "socket"), }, Body: decoder.Interface(inpReq, "body"), Cookies: decoder.Interface(inpReq, "cookies"), - Headers: decoder.Headers(inpReq), + Headers: decoder.Headers(inpReq, field("headers")), }, } } - if inpResp := decoder.MapStr(raw, "response"); inpResp != nil { + if inpResp := decoder.MapStr(raw, field("response")); inpResp != nil { if h == nil { h = &Http{} } @@ -336,7 +342,7 @@ func decodeHttp(raw common.MapStr, err error) (*Http, error) { Finished: decoder.BoolPtr(inpResp, "finished"), HeadersSent: decoder.BoolPtr(inpResp, "headers_sent"), } - minimalResp, err := DecodeMinimalHTTPResponse(raw, decoder.Err) + minimalResp, err := DecodeMinimalHTTPResponse(raw, hasShortFieldNames, decoder.Err) if err != nil { return nil, err } @@ -347,58 +353,63 @@ func decodeHttp(raw common.MapStr, err error) (*Http, error) { return h, decoder.Err } -func DecodeMinimalHTTPResponse(raw common.MapStr, err error) (*MinimalResp, error) { +func DecodeMinimalHTTPResponse(raw common.MapStr, hasShortFieldNames bool, err error) (*MinimalResp, error) { if err != nil { return nil, err } decoder := utility.ManualDecoder{} - inpResp := decoder.MapStr(raw, "response") + field := fields.Mapper(hasShortFieldNames) + + inpResp := decoder.MapStr(raw, field("response")) if inpResp == nil { return nil, nil } - headers := decoder.Headers(inpResp) + headers := decoder.Headers(inpResp, field("headers")) return &MinimalResp{ - StatusCode: decoder.IntPtr(inpResp, "status_code"), + StatusCode: decoder.IntPtr(inpResp, field("status_code")), Headers: headers, - DecodedBodySize: decoder.Float64Ptr(inpResp, "decoded_body_size"), - EncodedBodySize: decoder.Float64Ptr(inpResp, "encoded_body_size"), - TransferSize: decoder.Float64Ptr(inpResp, "transfer_size"), + DecodedBodySize: decoder.Float64Ptr(inpResp, field("decoded_body_size")), + EncodedBodySize: decoder.Float64Ptr(inpResp, field("encoded_body_size")), + TransferSize: decoder.Float64Ptr(inpResp, field("transfer_size")), }, decoder.Err } -func decodePage(raw common.MapStr, err error) (*Page, error) { +func decodePage(raw common.MapStr, hasShortFieldNames bool, err error) (*Page, error) { if err != nil { return nil, err } - pageInput, ok := raw["page"].(map[string]interface{}) + field := fields.Mapper(hasShortFieldNames) + pageInput, ok := raw[field("page")].(map[string]interface{}) if !ok { return nil, nil } decoder := utility.ManualDecoder{} return &Page{ - Url: decoder.StringPtr(pageInput, "url"), - Referer: decoder.StringPtr(pageInput, "referer"), + Url: decoder.StringPtr(pageInput, field("url")), + Referer: decoder.StringPtr(pageInput, field("referer")), }, decoder.Err } -func decodeLabels(raw common.MapStr, err error) (*Labels, error) { +func decodeLabels(raw common.MapStr, hasShortFieldNames bool, err error) (*Labels, error) { if err != nil { return nil, err } + field := fields.Mapper(hasShortFieldNames) decoder := utility.ManualDecoder{} - if l := decoder.MapStr(raw, "tags"); decoder.Err == nil && l != nil { + if l := decoder.MapStr(raw, field("tags")); decoder.Err == nil && l != nil { labels := Labels(l) return &labels, nil } return nil, decoder.Err } -func decodeCustom(raw common.MapStr, err error) (*Custom, error) { +func decodeCustom(raw common.MapStr, hasShortFieldNames bool, err error) (*Custom, error) { if err != nil { return nil, err } decoder := utility.ManualDecoder{} - if c := decoder.MapStr(raw, "custom"); decoder.Err == nil && c != nil { + field := fields.Mapper(hasShortFieldNames) + if c := decoder.MapStr(raw, field("custom")); decoder.Err == nil && c != nil { custom := Custom(c) return &custom, nil } diff --git a/model/error/event.go b/model/error/event.go index 439b737fdb9..8d105682574 100644 --- a/model/error/event.go +++ b/model/error/event.go @@ -28,6 +28,8 @@ import ( "strconv" "time" + "github.com/elastic/apm-server/model/fields" + "github.com/santhosh-tekuri/jsonschema" "github.com/elastic/beats/v7/libbeat/beat" @@ -138,9 +140,10 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor return nil, err } decoder := utility.ManualDecoder{} + field := fields.Mapper(cfg.HasShortFieldNames) e := Event{ Id: decoder.StringPtr(raw, "id"), - Culprit: decoder.StringPtr(raw, "culprit"), + Culprit: decoder.StringPtr(raw, field("culprit")), Labels: ctx.Labels, Page: ctx.Page, Http: ctx.Http, @@ -154,25 +157,25 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor TransactionId: decoder.StringPtr(raw, "transaction_id"), ParentId: decoder.StringPtr(raw, "parent_id"), TraceId: decoder.StringPtr(raw, "trace_id"), - TransactionSampled: decoder.BoolPtr(raw, "sampled", "transaction"), - TransactionType: decoder.StringPtr(raw, "type", "transaction"), + TransactionSampled: decoder.BoolPtr(raw, field("sampled"), field("transaction")), + TransactionType: decoder.StringPtr(raw, field("type"), field("transaction")), } - ex := decoder.MapStr(raw, "exception") - e.Exception = decodeException(&decoder)(ex) + ex := decoder.MapStr(raw, field("exception")) + e.Exception = decodeException(&decoder, cfg.HasShortFieldNames)(ex) - log := decoder.MapStr(raw, "log") - logMsg := decoder.StringPtr(log, "message") + log := decoder.MapStr(raw, field("log")) + logMsg := decoder.StringPtr(log, field("message")) if logMsg != nil { e.Log = &Log{ Message: *logMsg, - ParamMessage: decoder.StringPtr(log, "param_message"), - Level: decoder.StringPtr(log, "level"), - LoggerName: decoder.StringPtr(log, "logger_name"), + ParamMessage: decoder.StringPtr(log, field("param_message")), + Level: decoder.StringPtr(log, field("level")), + LoggerName: decoder.StringPtr(log, field("logger_name")), Stacktrace: m.Stacktrace{}, } var stacktrace *m.Stacktrace - stacktrace, decoder.Err = m.DecodeStacktrace(log["stacktrace"], decoder.Err) + stacktrace, decoder.Err = m.DecodeStacktrace(log[field("stacktrace")], cfg.HasShortFieldNames, decoder.Err) if stacktrace != nil { e.Log.Stacktrace = *stacktrace } @@ -423,7 +426,7 @@ func addStacktraceCounter(st m.Stacktrace) { type exceptionDecoder func(map[string]interface{}) *Exception -func decodeException(decoder *utility.ManualDecoder) exceptionDecoder { +func decodeException(decoder *utility.ManualDecoder, hasShortFieldNames bool) exceptionDecoder { var decode exceptionDecoder decode = func(exceptionTree map[string]interface{}) *Exception { exMsg := decoder.StringPtr(exceptionTree, "message") @@ -441,7 +444,7 @@ func decodeException(decoder *utility.ManualDecoder) exceptionDecoder { Stacktrace: m.Stacktrace{}, } var stacktrace *m.Stacktrace - stacktrace, decoder.Err = m.DecodeStacktrace(exceptionTree["stacktrace"], decoder.Err) + stacktrace, decoder.Err = m.DecodeStacktrace(exceptionTree["stacktrace"], hasShortFieldNames, decoder.Err) if stacktrace != nil { ex.Stacktrace = *stacktrace } diff --git a/model/error/generated/schema/rum_v3_error.go b/model/error/generated/schema/rum_v3_error.go index ad19654b462..46eb18277c1 100644 --- a/model/error/generated/schema/rum_v3_error.go +++ b/model/error/generated/schema/rum_v3_error.go @@ -44,7 +44,7 @@ const RUMV3Schema = `{ "type": ["string", "null"], "maxLength": 1024 }, - "transaction_id": { + "xid": { "type": ["string", "null"], "description": "Hex encoded 64 random bits ID of the correlated transaction. Must be present if trace_id and parent_id are set.", "maxLength": 1024 @@ -54,194 +54,151 @@ const RUMV3Schema = `{ "type": ["string", "null"], "maxLength": 1024 }, - "transaction": { + "x": { "type": ["object", "null"], "description": "Data for correlating errors with transactions", "properties": { - "sampled": { + "sm": { "type": ["boolean", "null"], "description": "Transactions that are 'sampled' will include all available information. Transactions that are not sampled will not have 'spans' or 'context'. Defaults to true." }, - "type": { + "t": { "type": ["string", "null"], "description": "Keyword of specific relevance in the service's domain (eg: 'request', 'backgroundjob', etc)", "maxLength": 1024 } } }, - "context": { - "$id": "doc/spec/context.json", + "c": { + "$id": "doc/spec/rum_v3_context.json", "title": "Context", "description": "Any arbitrary contextual information regarding the event, captured by the agent, optionally provided by the user", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "custom": { + "cu": { "description": "An arbitrary mapping of additional metadata to store with the event.", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "patternProperties": { "^[^.*\"]*$": {} }, "additionalProperties": false }, - "response": { - "type": ["object", "null"], + "r": { + "type": [ + "object", + "null" + ], "allOf": [ - { "$id": "doc/spec/http_response.json", - "title": "HTTP response object", - "description": "HTTP response object, used by error, span and transction documents", - "type": ["object", "null"], - "properties": { - "status_code": { - "type": ["integer", "null"], - "description": "The status code of the http request." - }, - "transfer_size": { - "type": ["number", "null"], - "description": "Total size of the payload." - }, - "encoded_body_size": { - "type": ["number", "null"], - "description": "The encoded size of the payload." - }, - "decoded_body_size": { - "type": ["number", "null"], - "description": "The decoded size of the payload." - }, - "headers": { - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } - } - } }, { "properties": { - "finished": { - "description": "A boolean indicating whether the response was finished or not", + "sc": { "type": [ - "boolean", + "integer", "null" - ] + ], + "description": "The status code of the http request." }, - "headers_sent": { + "ts": { "type": [ - "boolean", + "number", "null" - ] + ], + "description": "Total size of the payload." + }, + "ebs": { + "type": [ + "number", + "null" + ], + "description": "The encoded size of the payload." + }, + "dbs": { + "type": [ + "number", + "null" + ], + "description": "The decoded size of the payload." + }, + "he": { + "type": [ + "object", + "null" + ], + "patternProperties": { + "[.*]*$": { + "type": [ + "string", + "array", + "null" + ], + "items": { + "type": [ + "string" + ] + } + } + } } } } ] }, - "request": { - "$id": "docs/spec/http.json", - "title": "Request", - "description": "If a log record was generated as a result of a http request, the http interface can be used to collect this information.", - "type": ["object", "null"], - "properties": { - "body": { - "description": "Data should only contain the request body (not the query string). It can either be a dictionary (for standard HTTP requests) or a raw request body.", - "type": ["object", "string", "null"] - }, - "env": { - "description": "The env variable is a compounded of environment information passed from the webserver.", - "type": ["object", "null"], - "properties": {} - }, - "headers": { - "description": "Should include any headers sent by the requester. Cookies will be taken by headers if supplied.", - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } - }, - "http_version": { - "description": "HTTP version.", - "type": ["string", "null"], - "maxLength": 1024 - }, - "method": { - "description": "HTTP method.", - "type": "string", - "maxLength": 1024 - }, - "socket": { - "type": ["object", "null"], - "properties": { - "encrypted": { - "description": "Indicates whether request was sent as SSL/HTTPS request.", - "type": ["boolean", "null"] - }, - "remote_address": { - "description": "The network address sending the request. Should be obtained through standard APIs and not parsed from any headers like 'Forwarded'.", - "type": ["string", "null"] - } - } - }, - "url": { - "description": "A complete Url, with scheme, host and path.", - "type": "object", + "q": { "properties": { - "raw": { - "type": ["string", "null"], - "description": "The raw, unparsed URL of the HTTP request line, e.g https://example.com:443/search?q=elasticsearch. This URL may be absolute or relative. For more details, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2.", - "maxLength": 1024 + "en": { + "description": "The env variable is a compounded of environment information passed from the webserver.", + "type": [ + "object", + "null" + ], + "properties": {} }, - "protocol": { - "type": ["string", "null"], - "description": "The protocol of the request, e.g. 'https:'.", - "maxLength": 1024 - }, - "full": { - "type": ["string", "null"], - "description": "The full, possibly agent-assembled URL of the request, e.g https://example.com:443/search?q=elasticsearch#top.", - "maxLength": 1024 - }, - "hostname": { - "type": ["string", "null"], - "description": "The hostname of the request, e.g. 'example.com'.", - "maxLength": 1024 - }, - "port": { - "type": ["string", "integer","null"], - "description": "The port of the request, e.g. '443'", - "maxLength": 1024 - }, - "pathname": { - "type": ["string", "null"], - "description": "The path of the request, e.g. '/search'", - "maxLength": 1024 + "he": { + "description": "Should include any headers sent by the requester. Cookies will be taken by headers if supplied.", + "type": [ + "object", + "null" + ], + "patternProperties": { + "[.*]*$": { + "type": [ + "string", + "array", + "null" + ], + "items": { + "type": [ + "string" + ] + } + } + } }, - "search": { - "description": "The search describes the query string of the request. It is expected to have values delimited by ampersands.", - "type": ["string", "null"], + "hve": { + "description": "HTTP version.", + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "hash": { - "type": ["string", "null"], - "description": "The hash of the request URL, e.g. 'top'", + "mt": { + "description": "HTTP method.", + "type": "string", "maxLength": 1024 } - } - }, - "cookies": { - "description": "A parsed key-value object of cookies", - "type": ["object", "null"] - } - }, - "required": ["url", "method"] + }, + "required": [ + "mt" + ] }, - "tags": { + "g": { "$id": "doc/spec/tags.json", "title": "Tags", "type": ["object", "null"], @@ -254,293 +211,324 @@ const RUMV3Schema = `{ }, "additionalProperties": false }, - "user": { - "description": "Describes the correlated user for this event. If user data are provided here, all user related information from metadata is ignored, otherwise the metadata's user information will be stored with the event.", - "$id": "docs/spec/user.json", + "u": { + "$id": "docs/spec/rum_v3_user.json", "title": "User", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { "id": { "description": "Identifier of the logged in user, e.g. the primary key of the user", - "type": ["string", "integer", "null"], + "type": [ + "string", + "integer", + "null" + ], "maxLength": 1024 }, - "email": { + "em": { "description": "Email of the logged in user", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "username": { + "un": { "description": "The username of the logged in user", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "page": { + "p": { "description": "", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "referer": { + "rf": { "description": "RUM specific field that stores the URL of the page that 'linked' to the current page.", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "url": { "description": "RUM specific field that stores the URL of the current page", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] } } }, - "service": { + "se": { "description": "Service related information can be sent per event. Provided information will override the more generic information from metadata, non provided fields will be set according to the metadata information.", - "$id": "doc/spec/service.json", + "$id": "doc/spec/rum_v3_service.json", "title": "Service", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "agent": { + "a": { "description": "Name and version of the Elastic APM agent", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { + "n": { "description": "Name of the Elastic APM agent, e.g. \"Python\"", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { + "ve": { "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", - "type": ["string", "null"], - "maxLength": 1024 - }, - "ephemeral_id": { - "description": "Free format ID used for metrics correlation by some agents", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "framework": { + "fw": { "description": "Name and version of the web framework used", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "language": { + "la": { "description": "Name and version of the programming language used", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "name": { + "n": { "description": "Immutable name of the service emitting this event", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "pattern": "^[a-zA-Z0-9 _-]+$", "maxLength": 1024 }, - "environment": { + "en": { "description": "Environment name of the service, e.g. \"production\" or \"staging\"", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "runtime": { + "ru": { "description": "Name and version of the language runtime running this service", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "version": { + "ve": { "description": "Version of the service emitting this event", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 - }, - "node": { - "description": "Unique meaningful name of the service node.", - "type": ["object", "null"], - "properties": { - "configured_name": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - } - } - }, - "message": { - "$id": "doc/spec/message.json", - "title": "Message", - "description": "Details related to message receiving and publishing if the captured event integrates with a messaging system", - "type": ["object", "null"], - "properties": { - "queue": { - "type": ["object", "null"], - "properties": { - "name": { - "description": "Name of the message queue where the message is received.", - "type": ["string","null"], - "maxLength": 1024 - } - } - }, - "age": { - "type": ["object", "null"], - "properties": { - "ms": { - "description": "The age of the message in milliseconds. If the instrumented messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field.", - "type": ["integer", "null"] - } - } - }, - "body": { - "description": "messsage body, similar to an http request body", - "type": ["string", "null"] - }, - "headers": { - "description": "messsage headers, similar to http request headers", - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } } } } } }, - "culprit": { + "cu": { "description": "Function call which was the primary perpetrator of this event.", "type": ["string", "null"], "maxLength": 1024 }, - "exception": { + "ex": { "description": "Information about the originally thrown error.", "type": ["object", "null"], "properties": { - "code": { + "cd": { "type": ["string", "integer", "null"], "maxLength": 1024, "description": "The error code set when the error happened, e.g. database error code." }, - "message": { + "mg": { "description": "The original error message.", "type": ["string", "null"] }, - "module": { + "mo": { "description": "Describes the exception type's module namespace.", "type": ["string", "null"], "maxLength": 1024 }, - "attributes": { + "at": { "type": ["object", "null"] }, - "stacktrace": { + "st": { "type": ["array", "null"], "items": { - "$id": "docs/spec/stacktrace_frame.json", + "$id": "docs/spec/rum_v3_stacktrace_frame.json", "title": "Stacktrace", "type": "object", "description": "A stacktrace frame, contains various bits (most optional) describing the context of the frame", "properties": { - "abs_path": { + "ap": { "description": "The absolute path of the file involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "colno": { + "co": { "description": "Column number", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "context_line": { + "cli": { "description": "The line of code part of the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "filename": { + "f": { "description": "The relative filename of the code involved in the stack frame, used e.g. to do error checksumming", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "classname": { + "cn": { "description": "The classname of the code involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "function": { + "fn": { "description": "The function involved in the stack frame", - "type": ["string", "null"] - }, - "library_frame": { - "description": "A boolean, indicating if this frame is from a library or user code", - "type": ["boolean", "null"] + "type": [ + "string", + "null" + ] }, - "lineno": { + "li": { "description": "The line number of code part of the stack frame, used e.g. to do error checksumming", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "module": { + "mo": { "description": "The module to which frame belongs to", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "post_context": { + "poc": { "description": "The lines of code after the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } }, - "pre_context": { + "prc": { "description": "The lines of code before the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } - }, - "vars": { - "description": "Local variables for this stack frame", - "type": ["object", "null"], - "properties": {} } }, - "anyOf": [ - { "required": ["filename"], "properties": {"filename": { "type": "string" }} }, - { "required": ["classname"], "properties": {"classname": { "type": "string" }} } + "required": [ + "f" ] }, "minItems": 0 }, - "type": { + "t": { "type": ["string", "null"], "maxLength": 1024 }, - "handled": { + "hd": { "type": ["boolean", "null"], "description": "Indicator whether the error was caught somewhere in the code or not." }, - "cause": { + "ca": { "type": ["array", "null"], "items": { "type": ["object", "null"], @@ -551,115 +539,135 @@ const RUMV3Schema = `{ } }, "anyOf": [ - {"required": ["message"], "properties": {"message": {"type": "string"}}}, - {"required": ["type"], "properties": {"type": {"type": "string"}}} + {"required": ["mg"], "properties": {"mg": {"type": "string"}}}, + {"required": ["t"], "properties": {"t": {"type": "string"}}} ] }, "log": { "type": ["object", "null"], "description": "Additional information added when logging the error.", "properties": { - "level": { + "lv": { "description": "The severity of the record.", "type": ["string", "null"], "maxLength": 1024 }, - "logger_name": { + "ln": { "description": "The name of the logger instance used.", "type": ["string", "null"], "default": "default", "maxLength": 1024 }, - "message": { + "mg": { "description": "The additionally logged error message.", "type": "string" }, - "param_message": { + "pmg": { "description": "A parametrized message. E.g. 'Could not connect to %s'. The property message is still required, and should be equal to the param_message, but with placeholders replaced. In some situations the param_message is used to group errors together. The string is not interpreted, so feel free to use whichever placeholders makes sense in the client languange.", "type": ["string", "null"], "maxLength": 1024 }, - "stacktrace": { + "st": { "type": ["array", "null"], "items": { - "$id": "docs/spec/stacktrace_frame.json", + "$id": "docs/spec/rum_v3_stacktrace_frame.json", "title": "Stacktrace", "type": "object", "description": "A stacktrace frame, contains various bits (most optional) describing the context of the frame", "properties": { - "abs_path": { + "ap": { "description": "The absolute path of the file involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "colno": { + "co": { "description": "Column number", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "context_line": { + "cli": { "description": "The line of code part of the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "filename": { + "f": { "description": "The relative filename of the code involved in the stack frame, used e.g. to do error checksumming", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "classname": { + "cn": { "description": "The classname of the code involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "function": { + "fn": { "description": "The function involved in the stack frame", - "type": ["string", "null"] - }, - "library_frame": { - "description": "A boolean, indicating if this frame is from a library or user code", - "type": ["boolean", "null"] + "type": [ + "string", + "null" + ] }, - "lineno": { + "li": { "description": "The line number of code part of the stack frame, used e.g. to do error checksumming", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "module": { + "mo": { "description": "The module to which frame belongs to", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "post_context": { + "poc": { "description": "The lines of code after the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } }, - "pre_context": { + "prc": { "description": "The lines of code before the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } - }, - "vars": { - "description": "Local variables for this stack frame", - "type": ["object", "null"], - "properties": {} } }, - "anyOf": [ - { "required": ["filename"], "properties": {"filename": { "type": "string" }} }, - { "required": ["classname"], "properties": {"classname": { "type": "string" }} } + "required": [ + "f" ] }, "minItems": 0 } }, - "required": ["message"] + "required": ["mg"] } }, "allOf": [ { "required": ["id"] }, - { "if": {"required": ["transaction_id"], "properties": {"transaction_id": { "type": "string" }}}, + { "if": {"required": ["xid"], "properties": {"xid": { "type": "string" }}}, "then": { "required": ["trace_id", "parent_id"], "properties": {"trace_id": { "type": "string" }, "parent_id": {"type": "string"}}}}, { "if": {"required": ["trace_id"], "properties": {"trace_id": { "type": "string" }}}, "then": { "required": ["parent_id"], "properties": {"parent_id": { "type": "string" }}} }, @@ -667,7 +675,7 @@ const RUMV3Schema = `{ "then": { "required": ["trace_id"], "properties": {"trace_id": { "type": "string" }}} } ], "anyOf": [ - { "required": ["exception"], "properties": {"exception": { "type": "object" }} }, + { "required": ["ex"], "properties": {"ex": { "type": "object" }} }, { "required": ["log"], "properties": {"log": { "type": "object" }} } ] } diff --git a/model/fields/rum_v3_mapping.go b/model/fields/rum_v3_mapping.go new file mode 100644 index 00000000000..c72b3f341cf --- /dev/null +++ b/model/fields/rum_v3_mapping.go @@ -0,0 +1,132 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package fields + +var rumV3Mapping = map[string]string{ + "abs_path": "ap", + "action": "ac", + "address": "ad", + "agent": "a", + "attributes": "at", + "breakdown": "b", + "cause": "ca", + "classname": "cn", + "code": "cd", + "colno": "co", + "connectEnd": "ce", + "connectStart": "cs", + "context": "c", + "context_line": "cli", + "culprit": "cl", + "custom": "cu", + "decoded_body_size": "dbs", + "destination": "dt", + "domComplete": "dc", + "domContentLoadedEventEnd": "de", + "domContentLoadedEventStart": "ds", + "domInteractive": "di", + "domLoading": "dl", + "domainLookupEnd": "le", + "domainLookupStart": "ls", + "dropped": "dd", + "duration": "d", + "email": "em", + "encoded_body_size": "ebs", + "env": "en", + "environment": "en", + "error": "e", + "exception": "ex", + "fetchStart": "fs", + "filename": "f", + "firstContentfulPaint": "fp", + "framework": "fw", + "function": "fn", + "handled": "hd", + "headers": "he", + "http": "h", + "http_version": "hve", + "labels": "l", + "language": "la", + "largestContentfulPaint": "lp", + "level": "lv", + "lineno": "li", + "loadEventEnd": "ee", + "loadEventStart": "es", + "log": "log", + "logger_name": "ln", + "marks": "k", + "message": "mg", + "metadata": "m", + "method": "mt", + "metricset": "me", + "module": "mo", + "name": "n", + "navigationTiming": "nt", + "page": "p", + "param_message": "pmg", + "port": "po", + "post_context": "poc", + "pre_context": "prc", + "referer": "rf", + "request": "q", + "requestStart": "qs", + "resource": "rc", + "result": "rt", + "response": "r", + "responseEnd": "re", + "responseStart": "rs", + "runtime": "ru", + "sampled": "sm", + "samples": "sa", + "server-timing": "set", + "service": "se", + "span": "y", + "span.self_time.count": "ysc", + "span.self_time.sum.us": "yss", + "span_count": "yc", + "stacktrace": "st", + "start": "s", + "started": "sd", + "status_code": "sc", + "subType": "su", + "sync": "sy", + "tags": "g", + "timeToFirstByte": "fb", + "transaction": "x", + "transaction_id": "xid", + "transaction.breakdown.count": "xbc", + "transaction.duration.count": "xdc", + "transaction.duration.sum.us": "xds", + "transfer_size": "ts", + "type": "t", + "url": "url", + "user": "u", + "username": "un", + "value": "v", + "version": "ve", +} + +func Mapper(shortFieldNames bool) func(string) string { + return func(s string) string { + shortField, ok := rumV3Mapping[s] + if ok && shortFieldNames { + return shortField + } + return s + } +} diff --git a/model/message.go b/model/message.go index a2e172a99d5..87ebe62f634 100644 --- a/model/message.go +++ b/model/message.go @@ -52,7 +52,7 @@ func DecodeMessage(input interface{}, err error) (*Message, error) { m := Message{ QueueName: decoder.StringPtr(messageInp, "name", "queue"), Body: decoder.StringPtr(messageInp, "body"), - Headers: decoder.Headers(messageInp), + Headers: decoder.Headers(messageInp, "headers"), AgeMillis: decoder.IntPtr(messageInp, "ms", "age"), } if decoder.Err != nil { diff --git a/model/metadata/generated/schema/rum_v3_metadata.go b/model/metadata/generated/schema/rum_v3_metadata.go new file mode 100644 index 00000000000..d0961bd0137 --- /dev/null +++ b/model/metadata/generated/schema/rum_v3_metadata.go @@ -0,0 +1,233 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package schema + +const RUMV3Schema = `{ + "$id": "doc/spec/rum_v3_metadata.json", + "title": "Metadata", + "description": "Metadata concerning the other objects in the stream.", + "type": [ + "object" + ], + "properties": { + "se": { + "$id": "doc/spec/rum_v3_service.json", + "title": "Service", + "type": [ + "object", + "null" + ], + "properties": { + "a": { + "description": "Name and version of the Elastic APM agent", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "description": "Name of the Elastic APM agent, e.g. \"Python\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "fw": { + "description": "Name and version of the web framework used", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "la": { + "description": "Name and version of the programming language used", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "n": { + "description": "Immutable name of the service emitting this event", + "type": [ + "string", + "null" + ], + "pattern": "^[a-zA-Z0-9 _-]+$", + "maxLength": 1024 + }, + "en": { + "description": "Environment name of the service, e.g. \"production\" or \"staging\"", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ru": { + "description": "Name and version of the language runtime running this service", + "type": [ + "object", + "null" + ], + "properties": { + "n": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "ve": { + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "ve": { + "description": "Version of the service emitting this event", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + }, + "type": "object", + "required": [ + "n", + "a" + ], + "properties.n.type": "string", + "properties.a.type": "string", + "properties.a.required": [ + "n", + "ve" + ], + "properties.a.properties.n.type": "string", + "properties.a.properties.ve.type": "string", + "properties.ru.required": [ + "n", + "ve" + ], + "properties.ru.properties.n.type": "string", + "properties.ru.properties.ve.type": "string", + "properties.la.required": [ + "n" + ], + "properties.la.properties.n.type": "string" + }, + "u": { + "$id": "docs/spec/rum_v3_user.json", + "title": "User", + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "description": "Identifier of the logged in user, e.g. the primary key of the user", + "type": [ + "string", + "integer", + "null" + ], + "maxLength": 1024 + }, + "em": { + "description": "Email of the logged in user", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + }, + "un": { + "description": "The username of the logged in user", + "type": [ + "string", + "null" + ], + "maxLength": 1024 + } + } + }, + "l": { + "$id": "doc/spec/tags.json", + "title": "Tags", + "type": ["object", "null"], + "description": "A flat mapping of user-defined tags with string, boolean or number values.", + "patternProperties": { + "^[^.*\"]*$": { + "type": ["string", "boolean", "number", "null"], + "maxLength": 1024 + } + }, + "additionalProperties": false + } + }, + "required": [ + "se" + ] +} +` diff --git a/model/metadata/metadata.go b/model/metadata/metadata.go index e1b9e5453cd..de22d8c066a 100644 --- a/model/metadata/metadata.go +++ b/model/metadata/metadata.go @@ -20,6 +20,8 @@ package metadata import ( "errors" + "github.com/elastic/apm-server/model/fields" + "github.com/santhosh-tekuri/jsonschema" "github.com/elastic/beats/v7/libbeat/common" @@ -31,10 +33,16 @@ import ( var cachedModelSchema = validation.CreateSchema(schema.ModelSchema, "metadata") +var rumV3ModelSchema = validation.CreateSchema(schema.RUMV3Schema, "metadata") + func ModelSchema() *jsonschema.Schema { return cachedModelSchema } +func RUMV3ModelSchema() *jsonschema.Schema { + return rumV3ModelSchema +} + type Metadata struct { Service *Service Process *Process @@ -43,7 +51,7 @@ type Metadata struct { Labels common.MapStr } -func DecodeMetadata(input interface{}) (*Metadata, error) { +func DecodeMetadata(input interface{}, hasShortFieldNames bool) (*Metadata, error) { if input == nil { return nil, nil } @@ -52,17 +60,19 @@ func DecodeMetadata(input interface{}) (*Metadata, error) { return nil, errors.New("invalid type for metadata") } + field := fields.Mapper(hasShortFieldNames) + var err error var service *Service var system *System var process *Process var user *User var labels common.MapStr - service, err = DecodeService(raw["service"], err) + service, err = DecodeService(raw[field("service")], hasShortFieldNames, err) system, err = DecodeSystem(raw["system"], err) process, err = DecodeProcess(raw["process"], err) - user, err = DecodeUser(raw["user"], err) - labels, err = DecodeLabels(raw["labels"], err) + user, err = DecodeUser(raw[field("user")], hasShortFieldNames, err) + labels, err = DecodeLabels(raw[field("labels")], err) if err != nil { return nil, err diff --git a/model/metadata/metadata_test.go b/model/metadata/metadata_test.go index 20b2c39f3f8..00385627297 100644 --- a/model/metadata/metadata_test.go +++ b/model/metadata/metadata_test.go @@ -103,7 +103,7 @@ func TestDecodeMetadata(t *testing.T) { ), }, } { - metadata, err := DecodeMetadata(test.input) + metadata, err := DecodeMetadata(test.input, false) assert.Equal(t, test.err, err) assert.Equal(t, test.output, metadata) } diff --git a/model/metadata/service.go b/model/metadata/service.go index b887a0387fd..23c9f65798b 100644 --- a/model/metadata/service.go +++ b/model/metadata/service.go @@ -20,6 +20,8 @@ package metadata import ( "errors" + "github.com/elastic/apm-server/model/fields" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/apm-server/utility" @@ -67,7 +69,7 @@ type node struct { } //DecodeService decodes a given input into a Service instance -func DecodeService(input interface{}, err error) (*Service, error) { +func DecodeService(input interface{}, hasShortFieldNames bool, err error) (*Service, error) { if input == nil || err != nil { return nil, err } @@ -75,27 +77,28 @@ func DecodeService(input interface{}, err error) (*Service, error) { if !ok { return nil, errors.New("invalid type for service") } + field := fields.Mapper(hasShortFieldNames) decoder := utility.ManualDecoder{} service := Service{ - Name: decoder.StringPtr(raw, "name"), - Version: decoder.StringPtr(raw, "version"), - Environment: decoder.StringPtr(raw, "environment"), + Name: decoder.StringPtr(raw, field("name")), + Version: decoder.StringPtr(raw, field("version")), + Environment: decoder.StringPtr(raw, field("environment")), Agent: Agent{ - Name: decoder.StringPtr(raw, "name", "agent"), - Version: decoder.StringPtr(raw, "version", "agent"), + Name: decoder.StringPtr(raw, field("name"), field("agent")), + Version: decoder.StringPtr(raw, field("version"), field("agent")), EphemeralId: decoder.StringPtr(raw, "ephemeral_id", "agent"), }, Framework: Framework{ - Name: decoder.StringPtr(raw, "name", "framework"), - Version: decoder.StringPtr(raw, "version", "framework"), + Name: decoder.StringPtr(raw, field("name"), field("framework")), + Version: decoder.StringPtr(raw, field("version"), field("framework")), }, Language: Language{ - Name: decoder.StringPtr(raw, "name", "language"), - Version: decoder.StringPtr(raw, "version", "language"), + Name: decoder.StringPtr(raw, field("name"), field("language")), + Version: decoder.StringPtr(raw, field("version"), field("language")), }, Runtime: Runtime{ - Name: decoder.StringPtr(raw, "name", "runtime"), - Version: decoder.StringPtr(raw, "version", "runtime"), + Name: decoder.StringPtr(raw, field("name"), field("runtime")), + Version: decoder.StringPtr(raw, field("version"), field("runtime")), }, node: node{ name: decoder.StringPtr(raw, "configured_name", "node"), diff --git a/model/metadata/service_test.go b/model/metadata/service_test.go index 5b8aeadaba1..0f6c1be2ba8 100644 --- a/model/metadata/service_test.go +++ b/model/metadata/service_test.go @@ -183,7 +183,7 @@ func TestServiceDecode(t *testing.T) { }, }, } { - service, out := DecodeService(test.input, test.inpErr) + service, out := DecodeService(test.input, false, test.inpErr) assert.Equal(t, test.s, service) assert.Equal(t, test.err, out) } diff --git a/model/metadata/user.go b/model/metadata/user.go index dc918611f86..757286842bf 100644 --- a/model/metadata/user.go +++ b/model/metadata/user.go @@ -22,6 +22,8 @@ import ( "errors" "net" + "github.com/elastic/apm-server/model/fields" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/apm-server/utility" @@ -35,7 +37,7 @@ type User struct { UserAgent *string } -func DecodeUser(input interface{}, err error) (*User, error) { +func DecodeUser(input interface{}, hasShortFieldNames bool, err error) (*User, error) { if input == nil || err != nil { return nil, err } @@ -44,10 +46,11 @@ func DecodeUser(input interface{}, err error) (*User, error) { return nil, errors.New("invalid type for user") } decoder := utility.ManualDecoder{} + field := fields.Mapper(hasShortFieldNames) user := User{ UserAgent: decoder.StringPtr(raw, "user-agent"), - Name: decoder.StringPtr(raw, "username"), - Email: decoder.StringPtr(raw, "email"), + Name: decoder.StringPtr(raw, field("username")), + Email: decoder.StringPtr(raw, field("email")), IP: decoder.NetIP(raw, "ip"), } diff --git a/model/metadata/user_test.go b/model/metadata/user_test.go index 717f920f8d8..894c82227e4 100644 --- a/model/metadata/user_test.go +++ b/model/metadata/user_test.go @@ -149,7 +149,7 @@ func TestUserDecode(t *testing.T) { }, }, } { - user, err := DecodeUser(test.input, test.inputErr) + user, err := DecodeUser(test.input, false, test.inputErr) assert.Equal(t, test.u, user) assert.Equal(t, test.err, err) } diff --git a/model/span/event.go b/model/span/event.go index a61bb4448e9..7f88b1f7022 100644 --- a/model/span/event.go +++ b/model/span/event.go @@ -33,6 +33,7 @@ import ( "github.com/elastic/beats/v7/libbeat/monitoring" m "github.com/elastic/apm-server/model" + "github.com/elastic/apm-server/model/fields" "github.com/elastic/apm-server/model/metadata" "github.com/elastic/apm-server/model/span/generated/schema" "github.com/elastic/apm-server/transform" @@ -163,7 +164,7 @@ func (db *DB) fields() common.MapStr { return fields } -func decodeHTTP(input interface{}, err error) (*HTTP, error) { +func decodeHTTP(input interface{}, hasShortFieldNames bool, err error) (*HTTP, error) { if input == nil || err != nil { return nil, err } @@ -172,21 +173,22 @@ func decodeHTTP(input interface{}, err error) (*HTTP, error) { return nil, errors.New("invalid type for http") } decoder := utility.ManualDecoder{} - httpInput := decoder.MapStr(raw, "http") + field := fields.Mapper(hasShortFieldNames) + httpInput := decoder.MapStr(raw, field("http")) if decoder.Err != nil || httpInput == nil { return nil, decoder.Err } - method := decoder.StringPtr(httpInput, "method") + method := decoder.StringPtr(httpInput, field("method")) if method != nil { *method = strings.ToLower(*method) } - minimalResp, err := m.DecodeMinimalHTTPResponse(httpInput, decoder.Err) + minimalResp, err := m.DecodeMinimalHTTPResponse(httpInput, hasShortFieldNames, decoder.Err) if err != nil { return nil, err } return &HTTP{ - decoder.StringPtr(httpInput, "url"), - decoder.IntPtr(httpInput, "status_code"), + decoder.StringPtr(httpInput, field("url")), + decoder.IntPtr(httpInput, field("status_code")), method, minimalResp, }, nil @@ -213,7 +215,7 @@ func (http *HTTP) fields() common.MapStr { return fields } -func decodeDestination(input interface{}, err error) (*Destination, *DestinationService, error) { +func decodeDestination(input interface{}, hasShortFieldNames bool, err error) (*Destination, *DestinationService, error) { if input == nil || err != nil { return nil, nil, err } @@ -221,26 +223,27 @@ func decodeDestination(input interface{}, err error) (*Destination, *Destination if !ok { return nil, nil, errors.New("invalid type for destination") } + field := fields.Mapper(hasShortFieldNames) decoder := utility.ManualDecoder{} - destinationInput := decoder.MapStr(raw, "destination") + destinationInput := decoder.MapStr(raw, field("destination")) if decoder.Err != nil || destinationInput == nil { return nil, nil, decoder.Err } - serviceInput := decoder.MapStr(destinationInput, "service") + serviceInput := decoder.MapStr(destinationInput, field("service")) if decoder.Err != nil { return nil, nil, decoder.Err } var service *DestinationService if serviceInput != nil { service = &DestinationService{ - Type: decoder.StringPtr(serviceInput, "type"), - Name: decoder.StringPtr(serviceInput, "name"), - Resource: decoder.StringPtr(serviceInput, "resource"), + Type: decoder.StringPtr(serviceInput, field("type")), + Name: decoder.StringPtr(serviceInput, field("name")), + Resource: decoder.StringPtr(serviceInput, field("resource")), } } dest := Destination{ - Address: decoder.StringPtr(destinationInput, "address"), - Port: decoder.IntPtr(destinationInput, "port"), + Address: decoder.StringPtr(destinationInput, field("address")), + Port: decoder.IntPtr(destinationInput, field("port")), } return &dest, service, decoder.Err } @@ -288,26 +291,26 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor if !ok { return nil, errInvalidType } - + field := fields.Mapper(cfg.HasShortFieldNames) decoder := utility.ManualDecoder{} event := Event{ - Name: decoder.String(raw, "name"), - Start: decoder.Float64Ptr(raw, "start"), - Duration: decoder.Float64(raw, "duration"), - Sync: decoder.BoolPtr(raw, "sync"), - Timestamp: decoder.TimeEpochMicro(raw, "timestamp"), - Id: decoder.String(raw, "id"), + Name: decoder.String(raw, field("name")), + Start: decoder.Float64Ptr(raw, field("start")), + Duration: decoder.Float64(raw, field("duration")), + Sync: decoder.BoolPtr(raw, field("sync")), + Timestamp: decoder.TimeEpochMicro(raw, field("timestamp")), + Id: decoder.String(raw, field("id")), ParentId: decoder.String(raw, "parent_id"), TraceId: decoder.String(raw, "trace_id"), TransactionId: decoder.StringPtr(raw, "transaction_id"), - Type: decoder.String(raw, "type"), - Subtype: decoder.StringPtr(raw, "subtype"), - Action: decoder.StringPtr(raw, "action"), + Type: decoder.String(raw, field("type")), + Subtype: decoder.StringPtr(raw, field("subtype")), + Action: decoder.StringPtr(raw, field("action")), } - ctx := decoder.MapStr(raw, "context") + ctx := decoder.MapStr(raw, field("context")) if ctx != nil { - if labels, ok := ctx["tags"].(map[string]interface{}); ok { + if labels, ok := ctx[field("tags")].(map[string]interface{}); ok { event.Labels = labels } @@ -317,13 +320,13 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor } event.DB = db - http, err := decodeHTTP(ctx, decoder.Err) + http, err := decodeHTTP(ctx, cfg.HasShortFieldNames, decoder.Err) if err != nil { return nil, err } event.HTTP = http - dest, destService, err := decodeDestination(ctx, decoder.Err) + dest, destService, err := decodeDestination(ctx, cfg.HasShortFieldNames, decoder.Err) if err != nil { return nil, err } @@ -331,7 +334,7 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor event.DestinationService = destService if s, set := ctx["service"]; set { - service, err := metadata.DecodeService(s, decoder.Err) + service, err := metadata.DecodeService(s, cfg.HasShortFieldNames, decoder.Err) if err != nil { return nil, err } @@ -350,7 +353,7 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor } var stacktr *m.Stacktrace - stacktr, decoder.Err = m.DecodeStacktrace(raw["stacktrace"], decoder.Err) + stacktr, decoder.Err = m.DecodeStacktrace(raw[field("stacktrace")], cfg.HasShortFieldNames, decoder.Err) if decoder.Err != nil { return nil, decoder.Err } diff --git a/model/span/generated/schema/rum_v3_span.go b/model/span/generated/schema/rum_v3_span.go index cfd89e2dcc9..8160ef21cb0 100644 --- a/model/span/generated/schema/rum_v3_span.go +++ b/model/span/generated/schema/rum_v3_span.go @@ -22,36 +22,6 @@ const RUMV3Schema = `{ "type": "object", "description": "An event captured by an agent occurring in a monitored service", "allOf": [ - { "$id": "doc/spec/timestamp_epoch.json", - "title": "Timestamp Epoch", - "description": "Object with 'timestamp' property.", - "type": ["object"], - "properties": { - "timestamp": { - "description": "Recorded time of the event, UTC based and formatted as microseconds since Unix epoch", - "type": ["integer", "null"] - } - } }, - { "$id": "docs/spec/span_type.json", - "title": "Span Type", - "type": ["object"], - "properties": { - "type": { - "type": "string", - "description": "Keyword of specific relevance in the service's domain (eg: 'db.postgresql.query', 'template.erb', etc)", - "maxLength": 1024 - } - } }, - { "$id": "docs/spec/span_subtype.json", - "title": "Span Subtype", - "type": ["object"], - "properties": { - "subtype": { - "type": ["string", "null"], - "description": "A further sub-division of the type (e.g. postgresql, elasticsearch)", - "maxLength": 1024 - } - } }, { "properties": { "id": { @@ -60,7 +30,10 @@ const RUMV3Schema = `{ "maxLength": 1024 }, "transaction_id": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "Hex encoded 64 random bits ID of the correlated transaction.", "maxLength": 1024 }, @@ -74,107 +47,135 @@ const RUMV3Schema = `{ "type": "string", "maxLength": 1024 }, - "start": { - "type": ["number", "null"], + "s": { + "type": [ + "number", + "null" + ], "description": "Offset relative to the transaction's timestamp identifying the start of the span, in milliseconds" }, - "action": { - "type": ["string", "null"], + "t": { + "type": "string", + "description": "Keyword of specific relevance in the service's domain (eg: 'db.postgresql.query', 'template.erb', etc)", + "maxLength": 1024 + }, + "su": { + "type": [ + "string", + "null" + ], + "description": "A further sub-division of the type (e.g. postgresql, elasticsearch)", + "maxLength": 1024 + }, + "ac": { + "type": [ + "string", + "null" + ], "description": "The specific kind of event within the sub-type represented by the span (e.g. query, connect)", "maxLength": 1024 }, - "context": { - "type": ["object", "null"], + "c": { + "type": [ + "object", + "null" + ], "description": "Any other arbitrary data captured by the agent, optionally provided by the user", "properties": { - "destination": { - "type": ["object", "null"], + "dt": { + "type": [ + "object", + "null" + ], "description": "An object containing contextual data about the destination for spans", "properties": { - "address": { - "type": ["string", "null"], + "ad": { + "type": [ + "string", + "null" + ], "description": "Destination network address: hostname (e.g. 'localhost'), FQDN (e.g. 'elastic.co'), IPv4 (e.g. '127.0.0.1') or IPv6 (e.g. '::1')", "maxLength": 1024 }, - "port": { - "type": ["integer", "null"], + "po": { + "type": [ + "integer", + "null" + ], "description": "Destination network port (e.g. 443)" }, - "service": { + "se": { "description": "Destination service context", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "type": { + "t": { "description": "Type of the destination service (e.g. 'db', 'elasticsearch'). Should typically be the same as span.type.", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "name": { + "n": { "description": "Identifier for the destination service (e.g. 'http://elastic.co', 'elasticsearch', 'rabbitmq')", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "resource": { + "rc": { "description": "Identifier for the destination service resource being operated on (e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name')", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } }, - "required": ["type", "name", "resource"] - } - } - }, - "db": { - "type": ["object", "null"], - "description": "An object containing contextual data for database spans", - "properties": { - "instance": { - "type": ["string", "null"], - "description": "Database instance name" - }, - "link": { - "type": ["string", "null"], - "maxLength": 1024, - "description": "Database link" - }, - "statement": { - "type": ["string", "null"], - "description": "A database statement (e.g. query) for the given database type" - }, - "type": { - "type": ["string", "null"], - "description": "Database type. For any SQL database, \"sql\". For others, the lower-case database category, e.g. \"cassandra\", \"hbase\", or \"redis\"" - }, - "user": { - "type": ["string", "null"], - "description": "Username for accessing database" - }, - "rows_affected": { - "type": ["integer", "null"], - "description": "Number of rows affected by the SQL statement (if applicable)" + "required": [ + "t", + "n", + "rc" + ] } } }, - "http": { - "type": ["object", "null"], + "h": { + "type": [ + "object", + "null" + ], "description": "An object containing contextual data of the related http request.", "properties": { "url": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "description": "The raw url of the correlating http request." }, - "status_code": { - "type": ["integer", "null"], + "sc": { + "type": [ + "integer", + "null" + ], "description": "The status code of the http request." }, - "method": { - "type": ["string", "null"], + "mt": { + "type": [ + "string", + "null" + ], "maxLength": 1024, "description": "The method of the http request." } } }, - "tags": { + "g": { "$id": "doc/spec/tags.json", "title": "Tags", "type": ["object", "null"], @@ -187,17 +188,17 @@ const RUMV3Schema = `{ }, "additionalProperties": false }, - "service": { + "se": { "description": "Service related information can be sent per event. Provided information will override the more generic information from metadata, non provided fields will be set according to the metadata information.", "properties": { - "agent": { + "a": { "description": "Name and version of the Elastic APM agent", "type": [ "object", "null" ], "properties": { - "name": { + "n": { "description": "Name of the Elastic APM agent, e.g. \"Python\"", "type": [ "string", @@ -205,22 +206,17 @@ const RUMV3Schema = `{ ], "maxLength": 1024 }, - "version": { + "ve": { "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", "type": [ "string", "null" ], "maxLength": 1024 - }, - "ephemeral_id": { - "description": "Free format ID used for metrics correlation by some agents", - "type": ["string", "null"], - "maxLength": 1024 } } }, - "name": { + "n": { "description": "Immutable name of the service emitting this event", "type": [ "string", @@ -230,147 +226,141 @@ const RUMV3Schema = `{ "maxLength": 1024 } } - }, - "message": { - "$id": "doc/spec/message.json", - "title": "Message", - "description": "Details related to message receiving and publishing if the captured event integrates with a messaging system", - "type": ["object", "null"], - "properties": { - "queue": { - "type": ["object", "null"], - "properties": { - "name": { - "description": "Name of the message queue where the message is received.", - "type": ["string","null"], - "maxLength": 1024 - } - } - }, - "age": { - "type": ["object", "null"], - "properties": { - "ms": { - "description": "The age of the message in milliseconds. If the instrumented messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field.", - "type": ["integer", "null"] - } - } - }, - "body": { - "description": "messsage body, similar to an http request body", - "type": ["string", "null"] - }, - "headers": { - "description": "messsage headers, similar to http request headers", - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } - } - } } } }, - "duration": { + "d": { "type": "number", "description": "Duration of the span in milliseconds" }, - "name": { + "n": { "type": "string", "description": "Generic designation of a span in the scope of a transaction", "maxLength": 1024 }, - "stacktrace": { - "type": ["array", "null"], + "st": { + "type": [ + "array", + "null" + ], "description": "List of stack frames with variable attributes (eg: lineno, filename, etc)", "items": { - "$id": "docs/spec/stacktrace_frame.json", + "$id": "docs/spec/rum_v3_stacktrace_frame.json", "title": "Stacktrace", "type": "object", "description": "A stacktrace frame, contains various bits (most optional) describing the context of the frame", "properties": { - "abs_path": { + "ap": { "description": "The absolute path of the file involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "colno": { + "co": { "description": "Column number", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "context_line": { + "cli": { "description": "The line of code part of the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "filename": { + "f": { "description": "The relative filename of the code involved in the stack frame, used e.g. to do error checksumming", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "classname": { + "cn": { "description": "The classname of the code involved in the stack frame", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "function": { + "fn": { "description": "The function involved in the stack frame", - "type": ["string", "null"] - }, - "library_frame": { - "description": "A boolean, indicating if this frame is from a library or user code", - "type": ["boolean", "null"] + "type": [ + "string", + "null" + ] }, - "lineno": { + "li": { "description": "The line number of code part of the stack frame, used e.g. to do error checksumming", - "type": ["integer", "null"] + "type": [ + "integer", + "null" + ] }, - "module": { + "mo": { "description": "The module to which frame belongs to", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, - "post_context": { + "poc": { "description": "The lines of code after the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } }, - "pre_context": { + "prc": { "description": "The lines of code before the stack frame", - "type": ["array", "null"], + "type": [ + "array", + "null" + ], "minItems": 0, "items": { "type": "string" } - }, - "vars": { - "description": "Local variables for this stack frame", - "type": ["object", "null"], - "properties": {} } }, - "anyOf": [ - { "required": ["filename"], "properties": {"filename": { "type": "string" }} }, - { "required": ["classname"], "properties": {"classname": { "type": "string" }} } + "required": [ + "f" ] }, "minItems": 0 }, - "sync": { - "type": ["boolean", "null"], + "sy": { + "type": [ + "boolean", + "null" + ], "description": "Indicates whether the span was executed synchronously or asynchronously." } }, - "required": ["duration", "name", "type", "id","trace_id", "parent_id"] + "required": [ + "d", + "n", + "t", + "id", + "trace_id", + "parent_id" + ] }, - { "anyOf":[ - {"required": ["timestamp"], "properties": {"timestamp": { "type": "integer" }}}, - {"required": ["start"], "properties": {"start": { "type": "number" }}} - ] + { + "required": [ + "s" + ], + "properties": { + "s": { + "type": "number" + } + } } ] -} -` +}` diff --git a/model/stacktrace.go b/model/stacktrace.go index cfde82d64a4..93ce78bc20f 100644 --- a/model/stacktrace.go +++ b/model/stacktrace.go @@ -36,7 +36,7 @@ var ( type Stacktrace []*StacktraceFrame -func DecodeStacktrace(input interface{}, err error) (*Stacktrace, error) { +func DecodeStacktrace(input interface{}, hasShortFieldNames bool, err error) (*Stacktrace, error) { if input == nil || err != nil { return nil, err } @@ -46,7 +46,7 @@ func DecodeStacktrace(input interface{}, err error) (*Stacktrace, error) { } st := make(Stacktrace, len(raw)) for idx, fr := range raw { - st[idx], err = DecodeStacktraceFrame(fr, err) + st[idx], err = DecodeStacktraceFrame(fr, hasShortFieldNames, err) } return &st, err } diff --git a/model/stacktrace_frame.go b/model/stacktrace_frame.go index 7a5a71df08a..81067de7658 100644 --- a/model/stacktrace_frame.go +++ b/model/stacktrace_frame.go @@ -22,6 +22,8 @@ import ( "fmt" "regexp" + "github.com/elastic/apm-server/model/fields" + "github.com/pkg/errors" "github.com/elastic/beats/v7/libbeat/common" @@ -79,7 +81,7 @@ type Original struct { sourcemapCopied bool } -func DecodeStacktraceFrame(input interface{}, err error) (*StacktraceFrame, error) { +func DecodeStacktraceFrame(input interface{}, hasShortFieldNames bool, err error) (*StacktraceFrame, error) { if input == nil || err != nil { return nil, err } @@ -88,19 +90,20 @@ func DecodeStacktraceFrame(input interface{}, err error) (*StacktraceFrame, erro return nil, errInvalidStacktraceFrameType } decoder := utility.ManualDecoder{} + field := fields.Mapper(hasShortFieldNames) frame := StacktraceFrame{ - AbsPath: decoder.StringPtr(raw, "abs_path"), - Filename: decoder.StringPtr(raw, "filename"), - Classname: decoder.StringPtr(raw, "classname"), - Lineno: decoder.IntPtr(raw, "lineno"), - Colno: decoder.IntPtr(raw, "colno"), - ContextLine: decoder.StringPtr(raw, "context_line"), - Module: decoder.StringPtr(raw, "module"), - Function: decoder.StringPtr(raw, "function"), + AbsPath: decoder.StringPtr(raw, field("abs_path")), + Filename: decoder.StringPtr(raw, field("filename")), + Classname: decoder.StringPtr(raw, field("classname")), + Lineno: decoder.IntPtr(raw, field("lineno")), + Colno: decoder.IntPtr(raw, field("colno")), + ContextLine: decoder.StringPtr(raw, field("context_line")), + Module: decoder.StringPtr(raw, field("module")), + Function: decoder.StringPtr(raw, field("function")), LibraryFrame: decoder.BoolPtr(raw, "library_frame"), Vars: decoder.MapStr(raw, "vars"), - PreContext: decoder.StringArr(raw, "pre_context"), - PostContext: decoder.StringArr(raw, "post_context"), + PreContext: decoder.StringArr(raw, field("pre_context")), + PostContext: decoder.StringArr(raw, field("post_context")), } return &frame, decoder.Err } diff --git a/model/stacktrace_frame_test.go b/model/stacktrace_frame_test.go index 31dda291457..4263feca40e 100644 --- a/model/stacktrace_frame_test.go +++ b/model/stacktrace_frame_test.go @@ -92,7 +92,7 @@ func TestStacktraceFrameDecode(t *testing.T) { }, }, } { - frame, err := DecodeStacktraceFrame(test.input, test.inpErr) + frame, err := DecodeStacktraceFrame(test.input, false, test.inpErr) assert.Equal(t, test.s, frame) assert.Equal(t, test.err, err) } diff --git a/model/stacktrace_test.go b/model/stacktrace_test.go index f34f6e34830..fa76e686438 100644 --- a/model/stacktrace_test.go +++ b/model/stacktrace_test.go @@ -54,7 +54,7 @@ func TestStacktraceDecode(t *testing.T) { }, }, } { - s, err := DecodeStacktrace(test.input, test.inpErr) + s, err := DecodeStacktrace(test.input, false, test.inpErr) assert.Equal(t, test.s, s) assert.Equal(t, test.err, err) } diff --git a/model/transaction/event.go b/model/transaction/event.go index cba1513c750..36eca29e3a2 100644 --- a/model/transaction/event.go +++ b/model/transaction/event.go @@ -20,6 +20,8 @@ package transaction import ( "time" + "github.com/elastic/apm-server/model/fields" + "github.com/pkg/errors" "github.com/santhosh-tekuri/jsonschema" @@ -109,12 +111,13 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor return nil, err } decoder := utility.ManualDecoder{} + field := fields.Mapper(cfg.HasShortFieldNames) e := Event{ Id: decoder.String(raw, "id"), - Type: decoder.String(raw, "type"), - Name: decoder.StringPtr(raw, "name"), - Result: decoder.StringPtr(raw, "result"), - Duration: decoder.Float64(raw, "duration"), + Type: decoder.String(raw, field("type")), + Name: decoder.StringPtr(raw, field("name")), + Result: decoder.StringPtr(raw, field("result")), + Duration: decoder.Float64(raw, field("duration")), Labels: ctx.Labels, Page: ctx.Page, Http: ctx.Http, @@ -125,12 +128,12 @@ func DecodeEvent(input interface{}, cfg m.Config, err error) (transform.Transfor Client: ctx.Client, Experimental: ctx.Experimental, Message: ctx.Message, - Sampled: decoder.BoolPtr(raw, "sampled"), - Marks: decoder.MapStr(raw, "marks"), - Timestamp: decoder.TimeEpochMicro(raw, "timestamp"), + Sampled: decoder.BoolPtr(raw, field("sampled")), + Marks: decoder.MapStr(raw, field("marks")), + Timestamp: decoder.TimeEpochMicro(raw, field("timestamp")), SpanCount: SpanCount{ - Dropped: decoder.IntPtr(raw, "dropped", "span_count"), - Started: decoder.IntPtr(raw, "started", "span_count")}, + Dropped: decoder.IntPtr(raw, field("dropped"), field("span_count")), + Started: decoder.IntPtr(raw, field("started"), field("span_count"))}, ParentId: decoder.StringPtr(raw, "parent_id"), TraceId: decoder.String(raw, "trace_id"), } diff --git a/model/transaction/event_test.go b/model/transaction/event_test.go index 938fdaec0b4..846162c20f2 100644 --- a/model/transaction/event_test.go +++ b/model/transaction/event_test.go @@ -502,7 +502,7 @@ func TestEventsTransformWithMetadata(t *testing.T) { s, err := metadata.DecodeService(map[string]interface{}{ "name": "m-name", "version": "m-version", - "node": map[string]interface{}{"configured_name": serviceNodeName}}, nil) + "node": map[string]interface{}{"configured_name": serviceNodeName}}, false, nil) require.NoError(t, err) return s }(), diff --git a/model/transaction/generated/schema/rum_v3_transaction.go b/model/transaction/generated/schema/rum_v3_transaction.go index 3002a37e24d..4a8ace5eb43 100644 --- a/model/transaction/generated/schema/rum_v3_transaction.go +++ b/model/transaction/generated/schema/rum_v3_transaction.go @@ -22,36 +22,6 @@ const RUMV3Schema = `{ "type": "object", "description": "An event corresponding to an incoming request or similar task occurring in a monitored service", "allOf": [ - { "$id": "doc/spec/timestamp_epoch.json", - "title": "Timestamp Epoch", - "description": "Object with 'timestamp' property.", - "type": ["object"], - "properties": { - "timestamp": { - "description": "Recorded time of the event, UTC based and formatted as microseconds since Unix epoch", - "type": ["integer", "null"] - } - } }, - { "$id": "docs/spec/transaction_name.json", - "title": "Transaction Name", - "type": ["object"], - "properties": { - "name": { - "type": ["string","null"], - "description": "Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')", - "maxLength": 1024 - } - } }, - { "$id": "docs/spec/transaction_type.json", - "title": "Transaction Type", - "type": ["object"], - "properties": { - "type": { - "type": "string", - "description": "Keyword of specific relevance in the service's domain (eg: 'request', 'backgroundjob', etc)", - "maxLength": 1024 - } - } }, { "properties": { "id": { @@ -66,198 +36,174 @@ const RUMV3Schema = `{ }, "parent_id": { "description": "Hex encoded 64 random bits ID of the parent transaction or span. Only root transactions of a trace do not have a parent_id, otherwise it needs to be set.", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "span_count": { + "t": { + "type": "string", + "description": "Keyword of specific relevance in the service's domain (eg: 'request', 'backgroundjob', etc)", + "maxLength": 1024 + }, + "n": { + "type": [ + "string", + "null" + ], + "description": "Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')", + "maxLength": 1024 + }, + "yc": { "type": "object", "properties": { - "started": { + "sd": { "type": "integer", "description": "Number of correlated spans that are recorded." - }, - "dropped": { - "type": ["integer","null"], - "description": "Number of spans that have been dropped by the agent recording the transaction." - + "dd": { + "type": [ + "integer", + "null" + ], + "description": "Number of spans that have been dd by the a recording the x." } }, - "required": ["started"] + "required": [ + "sd" + ] }, - "context": { - "$id": "doc/spec/context.json", + "c": { + "$id": "doc/spec/rum_v3_context.json", "title": "Context", "description": "Any arbitrary contextual information regarding the event, captured by the agent, optionally provided by the user", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "custom": { + "cu": { "description": "An arbitrary mapping of additional metadata to store with the event.", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "patternProperties": { "^[^.*\"]*$": {} }, "additionalProperties": false }, - "response": { - "type": ["object", "null"], + "r": { + "type": [ + "object", + "null" + ], "allOf": [ - { "$id": "doc/spec/http_response.json", - "title": "HTTP response object", - "description": "HTTP response object, used by error, span and transction documents", - "type": ["object", "null"], - "properties": { - "status_code": { - "type": ["integer", "null"], - "description": "The status code of the http request." - }, - "transfer_size": { - "type": ["number", "null"], - "description": "Total size of the payload." - }, - "encoded_body_size": { - "type": ["number", "null"], - "description": "The encoded size of the payload." - }, - "decoded_body_size": { - "type": ["number", "null"], - "description": "The decoded size of the payload." - }, - "headers": { - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } - } - } }, { "properties": { - "finished": { - "description": "A boolean indicating whether the response was finished or not", + "sc": { + "type": [ + "integer", + "null" + ], + "description": "The status code of the http request." + }, + "ts": { + "type": [ + "number", + "null" + ], + "description": "Total size of the payload." + }, + "ebs": { "type": [ - "boolean", + "number", "null" - ] + ], + "description": "The encoded size of the payload." }, - "headers_sent": { + "dbs": { "type": [ - "boolean", + "number", "null" - ] + ], + "description": "The decoded size of the payload." + }, + "he": { + "type": [ + "object", + "null" + ], + "patternProperties": { + "[.*]*$": { + "type": [ + "string", + "array", + "null" + ], + "items": { + "type": [ + "string" + ] + } + } + } } } } ] }, - "request": { - "$id": "docs/spec/http.json", - "title": "Request", - "description": "If a log record was generated as a result of a http request, the http interface can be used to collect this information.", - "type": ["object", "null"], - "properties": { - "body": { - "description": "Data should only contain the request body (not the query string). It can either be a dictionary (for standard HTTP requests) or a raw request body.", - "type": ["object", "string", "null"] - }, - "env": { - "description": "The env variable is a compounded of environment information passed from the webserver.", - "type": ["object", "null"], - "properties": {} - }, - "headers": { - "description": "Should include any headers sent by the requester. Cookies will be taken by headers if supplied.", - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } - }, - "http_version": { - "description": "HTTP version.", - "type": ["string", "null"], - "maxLength": 1024 - }, - "method": { - "description": "HTTP method.", - "type": "string", - "maxLength": 1024 - }, - "socket": { - "type": ["object", "null"], - "properties": { - "encrypted": { - "description": "Indicates whether request was sent as SSL/HTTPS request.", - "type": ["boolean", "null"] - }, - "remote_address": { - "description": "The network address sending the request. Should be obtained through standard APIs and not parsed from any headers like 'Forwarded'.", - "type": ["string", "null"] - } - } - }, - "url": { - "description": "A complete Url, with scheme, host and path.", - "type": "object", + "q": { "properties": { - "raw": { - "type": ["string", "null"], - "description": "The raw, unparsed URL of the HTTP request line, e.g https://example.com:443/search?q=elasticsearch. This URL may be absolute or relative. For more details, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2.", - "maxLength": 1024 - }, - "protocol": { - "type": ["string", "null"], - "description": "The protocol of the request, e.g. 'https:'.", - "maxLength": 1024 - }, - "full": { - "type": ["string", "null"], - "description": "The full, possibly agent-assembled URL of the request, e.g https://example.com:443/search?q=elasticsearch#top.", - "maxLength": 1024 + "en": { + "description": "The env variable is a compounded of environment information passed from the webserver.", + "type": [ + "object", + "null" + ], + "properties": {} }, - "hostname": { - "type": ["string", "null"], - "description": "The hostname of the request, e.g. 'example.com'.", - "maxLength": 1024 - }, - "port": { - "type": ["string", "integer","null"], - "description": "The port of the request, e.g. '443'", - "maxLength": 1024 - }, - "pathname": { - "type": ["string", "null"], - "description": "The path of the request, e.g. '/search'", - "maxLength": 1024 + "he": { + "description": "Should include any headers sent by the requester. Cookies will be taken by headers if supplied.", + "type": [ + "object", + "null" + ], + "patternProperties": { + "[.*]*$": { + "type": [ + "string", + "array", + "null" + ], + "items": { + "type": [ + "string" + ] + } + } + } }, - "search": { - "description": "The search describes the query string of the request. It is expected to have values delimited by ampersands.", - "type": ["string", "null"], + "hve": { + "description": "HTTP version.", + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "hash": { - "type": ["string", "null"], - "description": "The hash of the request URL, e.g. 'top'", + "mt": { + "description": "HTTP method.", + "type": "string", "maxLength": 1024 } - } - }, - "cookies": { - "description": "A parsed key-value object of cookies", - "type": ["object", "null"] - } - }, - "required": ["url", "method"] + }, + "required": [ + "mt" + ] }, - "tags": { + "g": { "$id": "doc/spec/tags.json", "title": "Tags", "type": ["object", "null"], @@ -270,196 +216,213 @@ const RUMV3Schema = `{ }, "additionalProperties": false }, - "user": { - "description": "Describes the correlated user for this event. If user data are provided here, all user related information from metadata is ignored, otherwise the metadata's user information will be stored with the event.", - "$id": "docs/spec/user.json", + "u": { + "$id": "docs/spec/rum_v3_user.json", "title": "User", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { "id": { "description": "Identifier of the logged in user, e.g. the primary key of the user", - "type": ["string", "integer", "null"], + "type": [ + "string", + "integer", + "null" + ], "maxLength": 1024 }, - "email": { + "em": { "description": "Email of the logged in user", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "username": { + "un": { "description": "The username of the logged in user", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "page": { + "p": { "description": "", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "referer": { + "rf": { "description": "RUM specific field that stores the URL of the page that 'linked' to the current page.", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] }, "url": { "description": "RUM specific field that stores the URL of the current page", - "type": ["string", "null"] + "type": [ + "string", + "null" + ] } } }, - "service": { + "se": { "description": "Service related information can be sent per event. Provided information will override the more generic information from metadata, non provided fields will be set according to the metadata information.", - "$id": "doc/spec/service.json", + "$id": "doc/spec/rum_v3_service.json", "title": "Service", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "agent": { + "a": { "description": "Name and version of the Elastic APM agent", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { + "n": { "description": "Name of the Elastic APM agent, e.g. \"Python\"", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { + "ve": { "description": "Version of the Elastic APM agent, e.g.\"1.0.0\"", - "type": ["string", "null"], - "maxLength": 1024 - }, - "ephemeral_id": { - "description": "Free format ID used for metrics correlation by some agents", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "framework": { + "fw": { "description": "Name and version of the web framework used", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "language": { + "la": { "description": "Name and version of the programming language used", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "name": { + "n": { "description": "Immutable name of the service emitting this event", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "pattern": "^[a-zA-Z0-9 _-]+$", "maxLength": 1024 }, - "environment": { + "en": { "description": "Environment name of the service, e.g. \"production\" or \"staging\"", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "runtime": { + "ru": { "description": "Name and version of the language runtime running this service", - "type": ["object", "null"], + "type": [ + "object", + "null" + ], "properties": { - "name": { - "type": ["string", "null"], + "n": { + "type": [ + "string", + "null" + ], "maxLength": 1024 }, - "version": { - "type": ["string", "null"], + "ve": { + "type": [ + "string", + "null" + ], "maxLength": 1024 } } }, - "version": { + "ve": { "description": "Version of the service emitting this event", - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "maxLength": 1024 - }, - "node": { - "description": "Unique meaningful name of the service node.", - "type": ["object", "null"], - "properties": { - "configured_name": { - "type": ["string", "null"], - "maxLength": 1024 - } - } - } - } - }, - "message": { - "$id": "doc/spec/message.json", - "title": "Message", - "description": "Details related to message receiving and publishing if the captured event integrates with a messaging system", - "type": ["object", "null"], - "properties": { - "queue": { - "type": ["object", "null"], - "properties": { - "name": { - "description": "Name of the message queue where the message is received.", - "type": ["string","null"], - "maxLength": 1024 - } - } - }, - "age": { - "type": ["object", "null"], - "properties": { - "ms": { - "description": "The age of the message in milliseconds. If the instrumented messaging framework provides a timestamp for the message, agents may use it. Otherwise, the sending agent can add a timestamp in milliseconds since the Unix epoch to the message's metadata to be retrieved by the receiving agent. If a timestamp is not available, agents should omit this field.", - "type": ["integer", "null"] - } - } - }, - "body": { - "description": "messsage body, similar to an http request body", - "type": ["string", "null"] - }, - "headers": { - "description": "messsage headers, similar to http request headers", - "type": ["object", "null"], - "patternProperties": { - "[.*]*$": { - "type": ["string", "array", "null"], - "items": { - "type": ["string"] - } - } - } } } } } }, - "duration": { + "d": { "type": "number", "description": "How long the transaction took to complete, in ms with 3 decimal points" }, - "result": { - "type": ["string", "null"], + "rt": { + "type": [ + "string", + "null" + ], "description": "The result of the transaction. For HTTP-related transactions, this should be the status code formatted like 'HTTP 2xx'.", "maxLength": 1024 }, - "marks": { - "type": ["object", "null"], + "k": { + "type": [ + "object", + "null" + ], "description": "A mark captures the timing of a significant event during the lifetime of a transaction. Marks are organized into groups and can be set by the user or the agent.", "patternProperties": { "^[^.*\"]*$": { @@ -476,12 +439,21 @@ const RUMV3Schema = `{ }, "additionalProperties": false }, - "sampled": { - "type": ["boolean", "null"], + "sm": { + "type": [ + "boolean", + "null" + ], "description": "Transactions that are 'sampled' will include all available information. Transactions that are not sampled will not have 'spans' or 'context'. Defaults to true." } }, - "required": ["id", "trace_id", "span_count", "duration", "type"] + "required": [ + "id", + "trace_id", + "yc", + "d", + "t" + ] } ] } diff --git a/processor/stream/processor.go b/processor/stream/processor.go index 4afc46a3bbd..73d8e08d6e4 100644 --- a/processor/stream/processor.go +++ b/processor/stream/processor.go @@ -25,6 +25,8 @@ import ( "sync" "time" + "github.com/elastic/apm-server/model/fields" + "github.com/elastic/apm-server/beater/config" "github.com/elastic/apm-server/model" @@ -90,11 +92,12 @@ type processorModel struct { } type Processor struct { - Tconfig transform.Config - Mconfig model.Config - MaxEventSize int - bufferPool sync.Pool - models map[string]processorModel + Tconfig transform.Config + Mconfig model.Config + MaxEventSize int + bufferPool sync.Pool + models map[string]processorModel + metadataSchema *jsonschema.Schema } func BackendProcessor(cfg *config.Config) *Processor { @@ -116,10 +119,11 @@ func BackendProcessor(cfg *config.Config) *Processor { modelDecoder: metricset.DecodeEvent, }, "error": { - er.ModelSchema(), - er.DecodeEvent, + schema: er.ModelSchema(), + modelDecoder: er.DecodeEvent, }, }, + metadataSchema: metadata.ModelSchema(), } } @@ -142,32 +146,34 @@ func RUMProcessor(cfg *config.Config, tcfg *transform.Config) *Processor { modelDecoder: metricset.DecodeEvent, }, "error": { - er.ModelSchema(), - er.DecodeEvent, + schema: er.ModelSchema(), + modelDecoder: er.DecodeEvent, }, }, + metadataSchema: metadata.ModelSchema(), } } func RUMV3Processor(cfg *config.Config, tcfg *transform.Config) *Processor { return &Processor{ Tconfig: *tcfg, - Mconfig: model.Config{Experimental: cfg.Mode == config.ModeExperimental}, + Mconfig: model.Config{Experimental: cfg.Mode == config.ModeExperimental, HasShortFieldNames: true}, MaxEventSize: cfg.MaxEventSize, models: map[string]processorModel{ - "transaction": { + "x": { schema: transaction.RUMV3Schema, modelDecoder: transaction.DecodeRUMV3Event, }, - "span": { + "y": { schema: span.RUMV3Schema, modelDecoder: span.DecodeRUMV3Event, }, - "error": { + "e": { schema: er.RUMV3Schema, modelDecoder: er.DecodeRUMV3Event, }, }, + metadataSchema: metadata.RUMV3ModelSchema(), } } @@ -187,7 +193,8 @@ func (p *Processor) readMetadata(reqMeta map[string]interface{}, reader StreamRe return nil, err } - rawMetadata, ok := rawModel["metadata"].(map[string]interface{}) + field := fields.Mapper(p.Mconfig.HasShortFieldNames) + rawMetadata, ok := rawModel[field("metadata")].(map[string]interface{}) if !ok { return nil, &Error{ Type: InvalidInputErrType, @@ -201,7 +208,7 @@ func (p *Processor) readMetadata(reqMeta map[string]interface{}, reader StreamRe } // validate the metadata object against our jsonschema - err = validation.Validate(rawMetadata, metadata.ModelSchema()) + err = validation.Validate(rawMetadata, p.metadataSchema) if err != nil { return nil, &Error{ Type: InvalidInputErrType, @@ -211,7 +218,7 @@ func (p *Processor) readMetadata(reqMeta map[string]interface{}, reader StreamRe } // create a metadata struct - metadata, err := metadata.DecodeMetadata(rawMetadata) + metadata, err := metadata.DecodeMetadata(rawMetadata, p.Mconfig.HasShortFieldNames) if err != nil { return nil, err } diff --git a/processor/stream/processor_test.go b/processor/stream/processor_test.go index d3698bf153d..d098f38ec19 100644 --- a/processor/stream/processor_test.go +++ b/processor/stream/processor_test.go @@ -214,7 +214,7 @@ func TestRUMV3(t *testing.T) { name string }{ {path: "rum_errors.ndjson", name: "RUMV3Errors"}, - {path: "rum_transactions_spans.ndjson", name: "RUMV3Transactions"}, + // {path: "rum_events.ndjson", name: "RUMV3Events"}, } { t.Run(test.name, func(t *testing.T) { b, err := loader.LoadDataAsBytes(filepath.Join("../testdata/intake-v3/", test.path)) diff --git a/processor/stream/test_approved_es_documents/testIntakeRUMV3Errors.approved.json b/processor/stream/test_approved_es_documents/testIntakeRUMV3Errors.approved.json index 25f4f7ff46a..b98310b3598 100644 --- a/processor/stream/test_approved_es_documents/testIntakeRUMV3Errors.approved.json +++ b/processor/stream/test_approved_es_documents/testIntakeRUMV3Errors.approved.json @@ -1,111 +1,44 @@ { "events": [ { - "@timestamp": "2017-12-08T12:18:50.291Z", + "@timestamp": "2018-08-01T10:00:00Z", "agent": { - "name": "rum-js", - "version": "0.0.0" - }, - "client": { - "ip": "192.0.0.1" + "name": "js-base", + "version": "4.8.1" }, "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 - } - } - ] + "culprit": "test/e2e/general-usecase/app.e2e-bundle.min.js?token=secret", + "custom": { + "testContext": "testContext" }, + "grouping_key": "d41d8cd98f00b204e9800998ecf8427e", + "id": "3661352868c17c78b773d2f1beae6d41", "page": { - "referer": "http://localhost:8000/test/e2e/", - "url": "http://localhost:8000/test/e2e/general-usecase/" + "referer": "h://localhost:8000/test/e2e/", + "url": "h://localhost:8000/test/e2e/general-usecase/" } }, + "labels": { + "testTagKey": "testTagValue" + }, "processor": { "event": "error", "name": "error" }, "service": { - "name": "apm-agent-js", - "version": "1.0.1" + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" }, "timestamp": { - "us": 1512735530291000 + "us": 1533117600000000 }, - "user_agent": { - "original": "rum-2.0" + "user": { + "email": "em", + "id": "uId", + "name": "un" } } ] diff --git a/processor/stream/test_approved_es_documents/testIntakeRUMV3Events.approved.json b/processor/stream/test_approved_es_documents/testIntakeRUMV3Events.approved.json new file mode 100644 index 00000000000..fca749c51f6 --- /dev/null +++ b/processor/stream/test_approved_es_documents/testIntakeRUMV3Events.approved.json @@ -0,0 +1,512 @@ +{ + "events": [ + { + "@timestamp": "2018-08-01T10:00:00Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "http": { + "response": { + "decoded_body_size": 690, + "encoded_body_size": 690, + "transfer_size": 983 + } + }, + "labels": { + "testTagKey": "testTagValue" + }, + "processor": { + "event": "transaction", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "timestamp": { + "us": 1533117600000000 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + }, + "transaction": { + "custom": { + "testContext": "testContext" + }, + "duration": { + "us": 295000 + }, + "id": "ec2e280be8345240", + "marks": { + "a": { + "dc": 138, + "di": 120, + "fb": 5, + "fp": 70.825, + "lp": 131.03 + }, + "nt": { + "ce": 0, + "cs": 0, + "dc": 138, + "de": 122, + "di": 120, + "dl": 14, + "ds": 120, + "ee": 138, + "es": 138, + "fs": 0, + "le": 0, + "ls": 0, + "qs": 4, + "re": 6, + "rs": 5 + } + }, + "name": "general-usecase-initial-p-load", + "page": { + "referer": "h://localhost:8000/test/e2e/", + "url": "h://localhost:8000/test/e2e/general-usecase/" + }, + "sampled": true, + "span_count": { + "started": 8 + }, + "type": "p-load" + }, + "user": { + "email": "em", + "id": "uId", + "name": "un" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.004Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "duration": { + "us": 2000 + }, + "id": "bbd8bcc3be14d814", + "name": "Requesting and receiving the document", + "start": { + "us": 4000 + }, + "type": "hard-navigation" + }, + "timestamp": { + "us": 1533117600004000 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.014Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "duration": { + "us": 106000 + }, + "id": "fc546e87a90a774f", + "name": "Parsing the document, executing sy. scripts", + "start": { + "us": 14000 + }, + "type": "hard-navigation" + }, + "timestamp": { + "us": 1533117600014000 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.022534999Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "destination": { + "address": "localhost", + "port": 8000 + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "destination": { + "service": { + "name": "h://localhost:8000", + "resource": "localhost:8000", + "type": "rc" + } + }, + "duration": { + "us": 35060 + }, + "http": { + "response": { + "decoded_body_size": 676864, + "encoded_body_size": 676864, + "transfer_size": 677175 + }, + "url": { + "original": "h://localhost:8000/test/e2e/general-usecase/app.e2e-bundle.min.js?token=REDACTED" + } + }, + "id": "fb8f717930697299", + "name": "h://localhost:8000/test/e2e/general-usecase/app.e2e-bundle.min.js", + "servicemap": { + "fingerprint": "436ff629f7609f25" + }, + "start": { + "us": 22534 + }, + "type": "rc" + }, + "timestamp": { + "us": 1533117600022534 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.096929999Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "duration": { + "us": 198070 + }, + "id": "9b80535c4403c9fb", + "name": "OpenTracing y", + "start": { + "us": 96929 + }, + "type": "cu" + }, + "timestamp": { + "us": 1533117600096929 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.09894Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "destination": { + "address": "localhost", + "port": 8000 + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "destination": { + "service": { + "name": "h://localhost:8000", + "resource": "localhost:8000", + "type": "external" + } + }, + "duration": { + "us": 6724 + }, + "http": { + "method": "get", + "response": { + "status_code": 200 + }, + "url": { + "original": "h://localhost:8000/test/e2e/common/data.json?test=hamid" + } + }, + "id": "5ecb8ee030749715", + "name": "GET /test/e2e/common/data.json", + "servicemap": { + "fingerprint": "b46d2afefa4946e0" + }, + "start": { + "us": 98940 + }, + "sync": true, + "type": "external" + }, + "timestamp": { + "us": 1533117600098940 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.10652Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "destination": { + "address": "localhost", + "port": 8003 + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "destination": { + "service": { + "name": "h://localhost:8003", + "resource": "localhost:8003", + "type": "external" + } + }, + "duration": { + "us": 11584 + }, + "http": { + "method": "post", + "response": { + "status_code": 200 + }, + "url": { + "original": "h://localhost:8003/data" + } + }, + "id": "27f45fd274f976d4", + "name": "POST h://localhost:8003/data", + "servicemap": { + "fingerprint": "b46d2afefa4946e0" + }, + "start": { + "us": 106520 + }, + "sync": true, + "type": "external" + }, + "timestamp": { + "us": 1533117600106520 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.119935Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "destination": { + "address": "localhost", + "port": 8003 + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "destination": { + "service": { + "name": "h://localhost:8003", + "resource": "localhost:8003", + "type": "external" + } + }, + "duration": { + "us": 15949 + }, + "http": { + "method": "post", + "response": { + "status_code": 200 + }, + "url": { + "original": "h://localhost:8003/fetch" + } + }, + "id": "a3c043330bc2015e", + "name": "POST h://localhost:8003/fetch", + "servicemap": { + "fingerprint": "b46d2afefa4946e0" + }, + "start": { + "us": 119935 + }, + "sync": false, + "type": "external" + }, + "timestamp": { + "us": 1533117600119935 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + }, + { + "@timestamp": "2018-08-01T10:00:00.12Z", + "agent": { + "name": "js-base", + "version": "4.8.1" + }, + "labels": { + "testTagKey": "testTagValue" + }, + "parent": { + "id": "ec2e280be8345240" + }, + "processor": { + "event": "span", + "name": "transaction" + }, + "service": { + "language": { + "name": "javascript" + }, + "name": "apm-a-rum-test-e2e-general-usecase", + "version": "0.0.1" + }, + "span": { + "duration": { + "us": 2000 + }, + "id": "bc7665dc25629379", + "name": "Fire \"DOMContentLoaded\" event", + "start": { + "us": 120000 + }, + "type": "hard-navigation" + }, + "timestamp": { + "us": 1533117600120000 + }, + "trace": { + "id": "286ac3ad697892c406528f13c82e0ce1" + } + } + ] +} diff --git a/processor/stream/test_approved_es_documents/testIntakeRUMV3Transactions.approved.json b/processor/stream/test_approved_es_documents/testIntakeRUMV3Transactions.approved.json deleted file mode 100644 index 96afed90434..00000000000 --- a/processor/stream/test_approved_es_documents/testIntakeRUMV3Transactions.approved.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "events": [ - { - "@timestamp": "2018-08-01T10:00:00Z", - "agent": { - "name": "rum-js", - "version": "0.0.0" - }, - "client": { - "ip": "192.0.0.1" - }, - "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", - "page": { - "referer": "http://localhost:8000/test/e2e/", - "url": "http://localhost:8000/test/e2e/general-usecase/" - }, - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "page-load" - }, - "user_agent": { - "original": "rum-2.0" - } - }, - { - "@timestamp": "2018-08-01T10:00:00Z", - "agent": { - "name": "rum-js", - "version": "0.0.0" - }, - "client": { - "ip": "192.0.0.1" - }, - "parent": { - "id": "611f4fa950f04631" - }, - "processor": { - "event": "span", - "name": "transaction" - }, - "service": { - "name": "apm-agent-js", - "version": "1.0.0" - }, - "span": { - "duration": { - "us": 643000 - }, - "http": { - "url": { - "original": "http://localhost:8000/test/e2e/general-usecase/span" - } - }, - "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" - }, - "user_agent": { - "original": "rum-2.0" - } - } - ] -} diff --git a/script/inline_schemas/inline_schemas.go b/script/inline_schemas/inline_schemas.go index bc8cba3b9d0..3e58682fbce 100644 --- a/script/inline_schemas/inline_schemas.go +++ b/script/inline_schemas/inline_schemas.go @@ -35,6 +35,7 @@ func main() { }{ {"sourcemaps/payload.json", "model/sourcemap/generated/schema/payload.go", "PayloadSchema"}, {"metadata.json", "model/metadata/generated/schema/metadata.go", "ModelSchema"}, + {"rum_v3_metadata.json", "model/metadata/generated/schema/rum_v3_metadata.go", "RUMV3Schema"}, {"errors/error.json", "model/error/generated/schema/error.go", "ModelSchema"}, {"transactions/transaction.json", "model/transaction/generated/schema/transaction.go", "ModelSchema"}, {"spans/span.json", "model/span/generated/schema/span.go", "ModelSchema"}, diff --git a/utility/data_fetcher.go b/utility/data_fetcher.go index 1f69642811d..e9c1e6cc703 100644 --- a/utility/data_fetcher.go +++ b/utility/data_fetcher.go @@ -253,8 +253,9 @@ func (d *ManualDecoder) TimeEpochMicro(base map[string]interface{}, key string, return time.Time{} } -func (d *ManualDecoder) Headers(base map[string]interface{}) http.Header { - h := d.MapStr(base, "headers") +func (d *ManualDecoder) Headers(base map[string]interface{}, fieldName string) http.Header { + + h := d.MapStr(base, fieldName) if d.Err != nil || len(h) == 0 { return nil }