input/elasticapm/internal/modeldecoder/rumv3/model.go (317 lines of code) (raw):
// 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 rumv3
import (
"encoding/json"
"github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder/nullable"
)
var (
patternAlphaNumericExt = `^[a-zA-Z0-9 _-]+$`
enumOutcome = []string{"success", "failure", "unknown"}
)
// entry points
// errorRoot requires an error event to be present
type errorRoot struct {
Error errorEvent `json:"e" validate:"required"`
}
// metadatatRoot requires a metadata event to be present
type metadataRoot struct {
Metadata metadata `json:"m" validate:"required"`
}
// transactionRoot requires a transaction event to be present
type transactionRoot struct {
Transaction transaction `json:"x" validate:"required"`
}
// other structs
type context struct {
// Custom can contain additional metadata to be stored with the event.
// The format is unspecified and can be deeply nested objects.
// The information will not be indexed or searchable in Elasticsearch.
Custom map[string]any `json:"cu"`
// Tags are a flat mapping of user-defined tags. Allowed value types are
// string, boolean and number values. Tags are indexed and searchable.
Tags map[string]any `json:"g" validate:"inputTypesVals=string;bool;number,maxLengthVals=1024"`
// Service related information can be sent per event. Information provided
// here will override the more generic information retrieved from metadata,
// missing service fields will be retrieved from the metadata information.
Service contextService `json:"se"`
// User holds information about 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.
User user `json:"u"`
// Request describes the HTTP request information in case the event was
// created as a result of an HTTP request.
Request contextRequest `json:"q"`
// Page holds information related to the current page and page referers.
// It is only sent from RUM agents.
Page contextPage `json:"p"`
// Response describes the HTTP response information in case the event was
// created as a result of an HTTP request.
Response contextResponse `json:"r"`
}
type contextPage struct {
// Referer holds the URL of the page that 'linked' to the current page.
Referer nullable.String `json:"rf"`
// URL of the current page
URL nullable.String `json:"url"`
}
type contextRequest struct {
// Env holds environment variable information passed to the monitored service.
Env map[string]any `json:"en"`
// Headers includes any HTTP headers sent by the requester. Cookies will
// be taken by headers if supplied.
Headers nullable.HTTPHeader `json:"he"`
// HTTPVersion holds information about the used HTTP version.
HTTPVersion nullable.String `json:"hve" validate:"maxLength=1024"`
// Method holds information about the method of the HTTP request.
Method nullable.String `json:"mt" validate:"required,maxLength=1024"`
}
type contextResponse struct {
// Headers holds the http headers sent in the http response.
Headers nullable.HTTPHeader `json:"he"`
// DecodedBodySize holds the size of the decoded payload.
DecodedBodySize nullable.Int `json:"dbs"`
// EncodedBodySize holds the size of the encoded payload.
EncodedBodySize nullable.Int `json:"ebs"`
// StatusCode sent in the http response.
StatusCode nullable.Int `json:"sc"`
// TransferSize holds the total size of the payload.
TransferSize nullable.Int `json:"ts"`
}
type contextService struct {
// Agent holds information about the APM agent capturing the event.
Agent contextServiceAgent `json:"a"`
// Environment in which the monitored service is running,
// e.g. `production` or `staging`.
Environment nullable.String `json:"en" validate:"maxLength=1024"`
// Framework holds information about the framework used in the
// monitored service.
Framework contextServiceFramework `json:"fw"`
// Language holds information about the programming language of the
// monitored service.
Language contextServiceLanguage `json:"la"`
// Name of the monitored service.
Name nullable.String `json:"n" validate:"maxLength=1024,pattern=patternAlphaNumericExt"`
// Runtime holds information about the language runtime running the
// monitored service
Runtime contextServiceRuntime `json:"ru"`
// Version of the monitored service.
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type contextServiceAgent struct {
// Name of the APM agent capturing information.
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Version of the APM agent capturing information.
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type contextServiceFramework struct {
// Name of the used framework
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Version of the used framework
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type contextServiceLanguage struct {
// Name of the used programming language
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Version of the used programming language
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type contextServiceRuntime struct {
// Name of the language runtime
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Version of the language runtime
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type errorEvent struct {
_ struct{} `validate:"requiredAnyOf=ex;log"`
// Timestamp holds the recorded time of the event, UTC based and formatted
// as microseconds since Unix epoch.
Timestamp nullable.TimeMicrosUnix `json:"timestamp"`
// Log holds additional information added when the error is logged.
Log errorLog `json:"log"`
// Culprit identifies the function call which was the primary perpetrator
// of this event.
Culprit nullable.String `json:"cl" validate:"maxLength=1024"`
// ID holds the hex encoded 128 random bits ID of the event.
ID nullable.String `json:"id" validate:"required,maxLength=1024"`
// ParentID holds the hex encoded 64 random bits ID of the parent
// transaction or span.
ParentID nullable.String `json:"pid" validate:"requiredIfAny=xid;tid,maxLength=1024"`
// TraceID holds the hex encoded 128 random bits ID of the correlated trace.
TraceID nullable.String `json:"tid" validate:"requiredIfAny=xid;pid,maxLength=1024"`
// TransactionID holds the hex encoded 64 random bits ID of the correlated
// transaction.
TransactionID nullable.String `json:"xid" validate:"maxLength=1024"`
// Exception holds information about the original error.
// The information is language specific.
Exception errorException `json:"ex"`
// Transaction holds information about the correlated transaction.
Transaction errorTransactionRef `json:"x"`
// Context holds arbitrary contextual information for the event.
Context context `json:"c"`
}
type errorException struct {
_ struct{} `validate:"requiredAnyOf=mg;t"`
// Attributes of the exception.
Attributes map[string]any `json:"at"`
// Code that is set when the error happened, e.g. database error code.
Code nullable.Interface `json:"cd" validate:"inputTypes=string;int,maxLength=1024"`
// Cause can hold a collection of error exceptions representing chained
// exceptions. The chain starts with the outermost exception, followed
// by its cause, and so on.
Cause []errorException `json:"ca"`
// Message contains the originally captured error message.
Message nullable.String `json:"mg"`
// Module describes the exception type's module namespace.
Module nullable.String `json:"mo" validate:"maxLength=1024"`
// Stacktrace information of the captured exception.
Stacktrace []stacktraceFrame `json:"st"`
// Type of the exception.
Type nullable.String `json:"t" validate:"maxLength=1024"`
// Handled indicates whether the error was caught in the code or not.
Handled nullable.Bool `json:"hd"`
}
type errorLog struct {
// Level represents the severity of the recorded log.
Level nullable.String `json:"lv" validate:"maxLength=1024"`
// LoggerName holds the name of the used logger instance.
LoggerName nullable.String `json:"ln" validate:"maxLength=1024"`
// Message of the logged error. In case a parameterized message is captured,
// Message should contain the same information, but with any placeholders
// being replaced.
Message nullable.String `json:"mg" validate:"required"`
// ParamMessage should contain the same information as Message, but with
// placeholders where parameters were logged, e.g. 'error connecting to %s'.
// The string is not interpreted, allowing differnt placeholders per client
// languange. The information might be used to group errors together.
ParamMessage nullable.String `json:"pmg" validate:"maxLength=1024"`
// Stacktrace information of the captured error.
Stacktrace []stacktraceFrame `json:"st"`
}
type errorTransactionRef struct {
// Name is the generic designation of a transaction in the scope of a
// single service, eg: 'GET /users/:id'.
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Type expresses the correlated transaction's type as keyword that has
// specific relevance within the service's domain,
// eg: 'request', 'backgroundjob'.
Type nullable.String `json:"t" validate:"maxLength=1024"`
// Sampled indicates whether or not the full information for a transaction
// is captured. If a transaction is unsampled no spans and less context
// information will be reported.
Sampled nullable.Bool `json:"sm"`
}
type metadata struct {
// Labels are a flat mapping of user-defined tags. Allowed value types are
// string, boolean and number values. Labels are indexed and searchable.
Labels map[string]any `json:"l" validate:"inputTypesVals=string;bool;number,maxLengthVals=1024"`
// Service metadata about the monitored service.
Service metadataService `json:"se" validate:"required"`
// User metadata, which can be overwritten on a per event basis.
User user `json:"u"`
// Network holds information about the network over which the
// monitored service is communicating.
Network network `json:"n"`
}
type metadataService struct {
// Agent holds information about the APM agent capturing the event.
Agent metadataServiceAgent `json:"a" validate:"required"`
// Environment in which the monitored service is running,
// e.g. `production` or `staging`.
Environment nullable.String `json:"en" validate:"maxLength=1024"`
// Framework holds information about the framework used in the
// monitored service.
Framework metadataServiceFramework `json:"fw"`
// Language holds information about the programming language of the
// monitored service.
Language metadataServiceLanguage `json:"la"`
// Name of the monitored service.
Name nullable.String `json:"n" validate:"required,minLength=1,maxLength=1024,pattern=patternAlphaNumericExt"`
// Runtime holds information about the language runtime running the
// monitored service
Runtime metadataServiceRuntime `json:"ru"`
// Version of the monitored service.
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type metadataServiceAgent struct {
// Name of the APM agent capturing information.
Name nullable.String `json:"n" validate:"required,minLength=1,maxLength=1024"`
// Version of the APM agent capturing information.
Version nullable.String `json:"ve" validate:"required,maxLength=1024"`
}
type metadataServiceFramework struct {
// Name of the used framework
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Version of the used framework
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type metadataServiceLanguage struct {
// Name of the used programming language
Name nullable.String `json:"n" validate:"required,maxLength=1024"`
// Version of the used programming language
Version nullable.String `json:"ve" validate:"maxLength=1024"`
}
type metadataServiceRuntime struct {
// Name of the language runtime
Name nullable.String `json:"n" validate:"required,maxLength=1024"`
// Name of the language runtime
Version nullable.String `json:"ve" validate:"required,maxLength=1024"`
}
type network struct {
Connection networkConnection `json:"c"`
}
type networkConnection struct {
Type nullable.String `json:"t" validate:"maxLength=1024"`
}
type transactionMetricset struct {
// Span holds selected information about the correlated transaction.
Span metricsetSpanRef `json:"y"`
// Samples hold application metrics collected from the agent.
Samples transactionMetricsetSamples `json:"sa" validate:"required"`
}
type transactionMetricsetSamples struct {
// SpanSelfTimeCount holds the count of the related spans' self_time.
SpanSelfTimeCount metricsetSampleValue `json:"ysc"`
// SpanSelfTimeSum holds the sum of the related spans' self_time.
SpanSelfTimeSum metricsetSampleValue `json:"yss"`
}
type metricsetSampleValue struct {
// Value holds the value of a single metric sample.
Value nullable.Float64 `json:"v" validate:"required"`
}
type metricsetSpanRef struct {
// Subtype is a further sub-division of the type (e.g. postgresql, elasticsearch)
Subtype nullable.String `json:"su" validate:"maxLength=1024"`
// Type expresses the correlated span's type as keyword that has specific
// relevance within the service's domain, eg: 'request', 'backgroundjob'.
Type nullable.String `json:"t" validate:"maxLength=1024"`
}
type span struct {
// Name is the generic designation of a span in the scope of a transaction.
Name nullable.String `json:"n" validate:"required,maxLength=1024"`
// Stacktrace connected to this span event.
Stacktrace []stacktraceFrame `json:"st"`
// Type holds the span's type, and can have specific keywords
// within the service's domain (eg: 'request', 'backgroundjob', etc)
Type nullable.String `json:"t" validate:"required,maxLength=1024"`
// Subtype is a further sub-division of the type (e.g. postgresql, elasticsearch)
Subtype nullable.String `json:"su" validate:"maxLength=1024"`
// Action holds the specific kind of event within the sub-type represented
// by the span (e.g. query, connect)
Action nullable.String `json:"ac" validate:"maxLength=1024"`
// ID holds the hex encoded 64 random bits ID of the event.
ID nullable.String `json:"id" validate:"required,maxLength=1024"`
// Outcome of the span: success, failure, or unknown. Outcome may be one of
// a limited set of permitted values describing the success or failure of
// the span. It can be used for calculating error rates for outgoing requests.
Outcome nullable.String `json:"o" validate:"enum=enumOutcome"`
// Context holds arbitrary contextual information for the event.
Context spanContext `json:"c"`
// ParentIndex is the index of the parent span in the list. Absent when
// the parent is a transaction.
ParentIndex nullable.Int `json:"pi"`
// SampleRate applied to the monitored service at the time where this span
// was recorded.
SampleRate nullable.Float64 `json:"sr"`
// Start is the offset relative to the transaction's timestamp identifying
// the start of the span, in milliseconds.
Start nullable.Float64 `json:"s" validate:"required"`
// Duration of the span in milliseconds
Duration nullable.Float64 `json:"d" validate:"required,min=0"`
// Sync indicates whether the span was executed synchronously or asynchronously.
Sync nullable.Bool `json:"sy"`
}
type spanContext struct {
// Tags are a flat mapping of user-defined tags. Allowed value types are
// string, boolean and number values. Tags are indexed and searchable.
Tags map[string]any `json:"g" validate:"inputTypesVals=string;bool;number,maxLengthVals=1024"`
// Service related information can be sent per span. Information provided
// here will override the more generic information retrieved from metadata,
// missing service fields will be retrieved from the metadata information.
Service spanContextService `json:"se"`
// Destination contains contextual data about the destination of spans
Destination spanContextDestination `json:"dt"`
// HTTP contains contextual information when the span concerns an HTTP request.
HTTP spanContextHTTP `json:"h"`
}
type spanContextDestination struct {
// Service describes the destination service
Service spanContextDestinationService `json:"se"`
// Address is the destination network address:
// hostname (e.g. 'localhost'),
// FQDN (e.g. 'elastic.co'),
// IPv4 (e.g. '127.0.0.1')
// IPv6 (e.g. '::1')
Address nullable.String `json:"ad" validate:"maxLength=1024"`
// Port is the destination network port (e.g. 443)
Port nullable.Int `json:"po"`
}
type spanContextDestinationService struct {
// Name is the identifier for the destination service,
// e.g. 'http://elastic.co', 'elasticsearch', 'rabbitmq'
// DEPRECATED: this field will be removed in a future release
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Resource identifies the destination service resource being operated on
// e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'
// DEPRECATED: this field will be removed in a future release
Resource nullable.String `json:"rc" validate:"required,maxLength=1024"`
// Type of the destination service, e.g. db, elasticsearch. Should
// typically be the same as span.type.
// DEPRECATED: this field will be removed in a future release
Type nullable.String `json:"t" validate:"maxLength=1024"`
}
type spanContextHTTP struct {
// Method holds information about the method of the HTTP request.
Method nullable.String `json:"mt" validate:"maxLength=1024"`
// URL is the raw url of the correlating HTTP request.
URL nullable.String `json:"url"`
// Response describes the HTTP response information in case the event was
// created as a result of an HTTP request.
Response spanContextHTTPResponse `json:"r"`
// Deprecated: Use Response.StatusCode instead.
// StatusCode sent in the http response.
StatusCode nullable.Int `json:"sc"`
}
type spanContextHTTPResponse struct {
// DecodedBodySize holds the size of the decoded payload.
DecodedBodySize nullable.Int `json:"dbs"`
// EncodedBodySize holds the size of the encoded payload.
EncodedBodySize nullable.Int `json:"ebs"`
// TransferSize holds the total size of the payload.
TransferSize nullable.Int `json:"ts"`
}
type spanContextService struct {
// Agent holds information about the APM agent capturing the event.
Agent contextServiceAgent `json:"a"`
// Name of the monitored service.
Name nullable.String `json:"n" validate:"maxLength=1024,pattern=patternAlphaNumericExt"`
}
type stacktraceFrame struct {
// AbsPath is the absolute path of the frame's file.
AbsPath nullable.String `json:"ap"`
// Classname of the frame.
Classname nullable.String `json:"cn"`
// ContextLine is the line from the frame's file.
ContextLine nullable.String `json:"cli"`
// Filename is the relative name of the frame's file.
Filename nullable.String `json:"f" validate:"required"`
// Function represented by the frame.
Function nullable.String `json:"fn"`
// Module to which the frame belongs to.
Module nullable.String `json:"mo"`
// PostContext is a slice of code lines immediately before the line
// from the frame's file.
PostContext []string `json:"poc"`
// PreContext is a slice of code lines immediately after the line
// from the frame's file.
PreContext []string `json:"prc"`
// ColumnNumber of the frame.
ColumnNumber nullable.Int `json:"co"`
// LineNumber of the frame.
LineNumber nullable.Int `json:"li"`
}
type transaction struct {
// Marks capture 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. Marks are only reported by RUM agents.
Marks transactionMarks `json:"k"`
// TraceID holds the hex encoded 128 random bits ID of the correlated trace.
TraceID nullable.String `json:"tid" validate:"required,maxLength=1024"`
// Type expresses the transaction's type as keyword that has specific
// relevance within the service's domain, eg: 'request', 'backgroundjob'.
Type nullable.String `json:"t" validate:"required,maxLength=1024"`
// Spans is a collection of spans related to this transaction.
Spans []span `json:"y"`
// Metricsets is a collection metrics related to this transaction.
Metricsets []transactionMetricset `json:"me"`
// Result of the transaction. For HTTP-related transactions, this should
// be the status code formatted like 'HTTP 2xx'.
Result nullable.String `json:"rt" validate:"maxLength=1024"`
// ID holds the hex encoded 64 random bits ID of the event.
ID nullable.String `json:"id" validate:"required,maxLength=1024"`
// Name is the generic designation of a transaction in the scope of a
// single service, eg: 'GET /users/:id'.
Name nullable.String `json:"n" validate:"maxLength=1024"`
// Outcome of the transaction with a limited set of permitted values,
// describing the success or failure of the transaction from the service's
// perspective. It is used for calculating error rates for incoming requests.
// Permitted values: success, failure, unknown.
Outcome nullable.String `json:"o" validate:"enum=enumOutcome"`
// ParentID holds the hex encoded 64 random bits ID of the parent
// transaction or span.
ParentID nullable.String `json:"pid" validate:"maxLength=1024"`
// Session holds optional transaction session information for RUM.
Session transactionSession `json:"ses"`
// Context holds arbitrary contextual information for the event.
Context context `json:"c"`
// UserExperience holds metrics for measuring real user experience.
// This information is only sent by RUM agents.
UserExperience transactionUserExperience `json:"exp"`
// SpanCount counts correlated spans.
SpanCount transactionSpanCount `json:"yc" validate:"required"`
// SampleRate applied to the monitored service at the time where this transaction
// was recorded. Allowed values are [0..1]. A SampleRate <1 indicates that
// not all spans are recorded.
SampleRate nullable.Float64 `json:"sr"`
// Duration how long the transaction took to complete, in milliseconds
// with 3 decimal points.
Duration nullable.Float64 `json:"d" validate:"required,min=0"`
// Sampled indicates whether or not the full information for a transaction
// is captured. If a transaction is unsampled no spans and less context
// information will be reported.
Sampled nullable.Bool `json:"sm"`
}
type transactionSession struct {
// ID holds a session ID for grouping a set of related transactions.
ID nullable.String `json:"id" validate:"required"`
// Sequence holds an optional sequence number for a transaction within
// a session. It is not meaningful to compare sequences across two
// different sessions.
Sequence nullable.Int `json:"seq" validate:"min=1"`
}
type transactionMarks struct {
Events map[string]transactionMarkEvents `json:"-"`
}
var markEventsLongNames = map[string]string{
"a": "agent",
"nt": "navigationTiming",
}
func (m *transactionMarks) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &m.Events); err != nil {
return err
}
for name, val := range m.Events {
nameLong, ok := markEventsLongNames[name]
if !ok {
// there is no long name defined for this event
continue
}
delete(m.Events, name)
m.Events[nameLong] = val
}
return nil
}
type transactionMarkEvents struct {
Measurements map[string]float64 `json:"-"`
}
func (m *transactionMarkEvents) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &m.Measurements); err != nil {
return err
}
for name, val := range m.Measurements {
nameLong, ok := markMeasurementsLongNames[name]
if !ok {
// there is no long name defined for this measurement
continue
}
delete(m.Measurements, name)
m.Measurements[nameLong] = val
}
return nil
}
var markMeasurementsLongNames = map[string]string{
"ce": "connectEnd",
"cs": "connectStart",
"dc": "domComplete",
"de": "domContentLoadedEventEnd",
"di": "domInteractive",
"dl": "domLoading",
"ds": "domContentLoadedEventStart",
"ee": "loadEventEnd",
"es": "loadEventStart",
"fb": "timeToFirstByte",
"fp": "firstContentfulPaint",
"fs": "fetchStart",
"le": "domainLookupEnd",
"lp": "largestContentfulPaint",
"ls": "domainLookupStart",
"re": "responseEnd",
"rs": "responseStart",
"qs": "requestStart",
}
type transactionSpanCount struct {
// Dropped is the number of correlated spans that have been dropped by
// the APM agent recording the transaction.
Dropped nullable.Int `json:"dd"`
// Started is the number of correlated spans that are recorded.
Started nullable.Int `json:"sd" validate:"required"`
}
// userExperience holds real user (browser) experience metrics.
type transactionUserExperience struct {
// CumulativeLayoutShift holds the Cumulative Layout Shift (CLS) metric value,
// or a negative value if CLS is unknown. See https://web.dev/cls/
CumulativeLayoutShift nullable.Float64 `json:"cls" validate:"min=0"`
// FirstInputDelay holds the First Input Delay (FID) metric value,
// or a negative value if FID is unknown. See https://web.dev/fid/
FirstInputDelay nullable.Float64 `json:"fid" validate:"min=0"`
// TotalBlockingTime holds the Total Blocking Time (TBT) metric value,
// or a negative value if TBT is unknown. See https://web.dev/tbt/
TotalBlockingTime nullable.Float64 `json:"tbt" validate:"min=0"`
// Longtask holds longtask duration/count metrics.
Longtask longtaskMetrics `json:"lt"`
}
type longtaskMetrics struct {
// Count is the total number of of longtasks.
Count nullable.Int `json:"count" validate:"required,min=0"`
// Max longtask duration
Max nullable.Float64 `json:"max" validate:"required,min=0"`
// Sum of longtask durations
Sum nullable.Float64 `json:"sum" validate:"required,min=0"`
}
type user struct {
// Domain of the user
Domain nullable.String `json:"ud" validate:"maxLength=1024"`
// ID identifies the logged in user, e.g. can be the primary key of the user
ID nullable.Interface `json:"id" validate:"maxLength=1024,inputTypes=string;int"`
// Email of the user.
Email nullable.String `json:"em" validate:"maxLength=1024"`
// Name of the user.
Name nullable.String `json:"un" validate:"maxLength=1024"`
}