From 62a18ecbc1bdd30285174a7a621e59474e0f3dc7 Mon Sep 17 00:00:00 2001 From: kruskall <99559985+kruskall@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:43:50 +0200 Subject: [PATCH] feat: add model package and drop go agent (#423) * feat: add model package and drop go agent * build: drop go agent dependency * build: regenerate notice file --- NOTICE.txt | 211 ----------------------------------- dependencies.asciidoc | 1 - go.mod | 1 - go.sum | 2 - logsapi/functionlogs.go | 68 +---------- logsapi/functionlogs_test.go | 6 +- logsapi/metrics.go | 34 +----- logsapi/model/model.go | 89 +++++++++++++++ logsapi/model/model_json.go | 157 ++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 313 deletions(-) create mode 100644 logsapi/model/model.go create mode 100644 logsapi/model/model_json.go diff --git a/NOTICE.txt b/NOTICE.txt index d6277582..bc617353 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -3347,217 +3347,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- -Module : go.elastic.co/apm/v2 -Version : v2.6.0 -Time : 2024-04-11T12:45:42Z -Licence : Apache-2.0 - -Contents of probable licence file $GOMODCACHE/go.elastic.co/apm/v2@v2.6.0/LICENSE: - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2018 Elasticsearch BV - - Licensed 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. - - -------------------------------------------------------------------------------- Module : go.elastic.co/ecszap Version : v1.0.2 diff --git a/dependencies.asciidoc b/dependencies.asciidoc index 9278a3ef..bf7e3b73 100644 --- a/dependencies.asciidoc +++ b/dependencies.asciidoc @@ -35,7 +35,6 @@ This page lists the third-party dependencies used to build {n}. | link:https://github.com/tidwall/match[$$github.com/tidwall/match$$] | v1.1.1 | MIT | link:https://github.com/tidwall/pretty[$$github.com/tidwall/pretty$$] | v1.2.1 | MIT | link:https://github.com/tidwall/sjson[$$github.com/tidwall/sjson$$] | v1.2.5 | MIT -| link:https://go.elastic.co/apm/v2[$$go.elastic.co/apm/v2$$] | v2.4.3 | Apache-2.0 | link:https://go.elastic.co/ecszap[$$go.elastic.co/ecszap$$] | v1.0.2 | Apache-2.0 | link:https://go.elastic.co/fastjson[$$go.elastic.co/fastjson$$] | v1.3.0 | MIT | link:https://go.uber.org/multierr[$$go.uber.org/multierr$$] | v1.10.0 | MIT diff --git a/go.mod b/go.mod index cab7acfb..284336b4 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/stretchr/testify v1.9.0 github.com/tidwall/gjson v1.17.1 github.com/tidwall/sjson v1.2.5 - go.elastic.co/apm/v2 v2.6.0 go.elastic.co/ecszap v1.0.2 go.elastic.co/fastjson v1.3.0 go.uber.org/zap v1.27.0 diff --git a/go.sum b/go.sum index f285622b..a878274d 100644 --- a/go.sum +++ b/go.sum @@ -72,8 +72,6 @@ github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhso github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.elastic.co/apm/v2 v2.6.0 h1:VieBMLQFtXua2YxpYxaSdYGnmmxhLT46gosI5yErJgY= -go.elastic.co/apm/v2 v2.6.0/go.mod h1:33rOXgtHwbgZcDgi6I/GtCSMZQqgxkHC0IQT3gudKvo= go.elastic.co/ecszap v1.0.2 h1:iW5OGx8IiokiUzx/shD4AJCPFMC9uUtr7ycaiEIU++I= go.elastic.co/ecszap v1.0.2/go.mod h1:dJkSlK3BTiwG/qXhCwe50Mz/jwu854vSip8sIeQhNZg= go.elastic.co/fastjson v1.3.0 h1:hJO3OsYIhiqiT4Fgu0ZxAECnKASbwgiS+LMW5oCopKs= diff --git a/logsapi/functionlogs.go b/logsapi/functionlogs.go index 2f3fc5cd..45dae12f 100644 --- a/logsapi/functionlogs.go +++ b/logsapi/functionlogs.go @@ -18,70 +18,10 @@ package logsapi import ( - "go.elastic.co/apm/v2/model" + "github.com/elastic/apm-aws-lambda/logsapi/model" "go.elastic.co/fastjson" ) -type logContainer struct { - Log *logLine -} - -type logLine struct { - Timestamp model.Time - Message string - FAAS *faas -} - -func (l *logLine) MarshalFastJSON(w *fastjson.Writer) error { - var firstErr error - w.RawString("{\"message\":") - w.String(l.Message) - w.RawString(",\"@timestamp\":") - if err := l.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { - firstErr = err - } - if l.FAAS != nil { - w.RawString(",\"faas\":") - if err := l.FAAS.MarshalFastJSON(w); err != nil && firstErr == nil { - firstErr = err - } - } - w.RawByte('}') - return firstErr -} - -// faas struct is a subset of go.elastic.co/apm/v2/model#FAAS -// -// The purpose of having a separate struct is to have a custom -// marshalling logic that is targeted for the faas fields -// available for function logs. For example: `coldstart` value -// cannot be inferred for function logs so this struct drops -// the field entirely. -type faas struct { - // ID holds a unique identifier of the invoked serverless function. - ID string `json:"id,omitempty"` - // Execution holds the request ID of the function invocation. - Execution string `json:"execution,omitempty"` -} - -func (f *faas) MarshalFastJSON(w *fastjson.Writer) error { - w.RawString("{\"id\":") - w.String(f.ID) - w.RawString(",\"execution\":") - w.String(f.Execution) - w.RawByte('}') - return nil -} - -func (lc logContainer) MarshalFastJSON(json *fastjson.Writer) error { - json.RawString(`{"log":`) - if err := lc.Log.MarshalFastJSON(json); err != nil { - return err - } - json.RawByte('}') - return nil -} - // ProcessFunctionLog processes the `function` log line from lambda logs API and returns // a byte array containing the JSON body for the extracted log along with the timestamp. // A non nil error is returned when marshaling of the log into JSON fails. @@ -90,14 +30,14 @@ func ProcessFunctionLog( invokedFnArn string, log LogEvent, ) ([]byte, error) { - lc := logContainer{ - Log: &logLine{ + lc := model.LogContainer{ + Log: &model.LogLine{ Timestamp: model.Time(log.Time), Message: log.StringRecord, }, } - lc.Log.FAAS = &faas{ + lc.Log.FAAS = &model.FAAS{ ID: invokedFnArn, Execution: requestID, } diff --git a/logsapi/functionlogs_test.go b/logsapi/functionlogs_test.go index 7d8661e3..ff31c389 100644 --- a/logsapi/functionlogs_test.go +++ b/logsapi/functionlogs_test.go @@ -35,11 +35,11 @@ func TestProcessFunctionLog(t *testing.T) { reqID := "8476a536-e9f4-11e8-9739-2dfe598c3fcd" invokedFnArn := "arn:aws:lambda:us-east-2:123456789012:function:custom-runtime" expectedData := fmt.Sprintf( - "{\"log\":{\"message\":\"%s\",\"@timestamp\":%d,\"faas\":{\"id\":\"%s\",\"execution\":\"%s\"}}}", - event.StringRecord, + "{\"log\":{\"@timestamp\":%d,\"message\":\"%s\",\"faas\":{\"execution\":\"%s\",\"id\":\"%s\"}}}", event.Time.UnixNano()/int64(time.Microsecond), - invokedFnArn, + event.StringRecord, reqID, + invokedFnArn, ) data, err := ProcessFunctionLog(reqID, invokedFnArn, event) diff --git a/logsapi/metrics.go b/logsapi/metrics.go index 2a26aa48..4e65846b 100644 --- a/logsapi/metrics.go +++ b/logsapi/metrics.go @@ -21,7 +21,7 @@ import ( "math" "time" - "go.elastic.co/apm/v2/model" + "github.com/elastic/apm-aws-lambda/logsapi/model" "go.elastic.co/fastjson" ) @@ -33,39 +33,11 @@ type PlatformMetrics struct { InitDurationMs float32 `json:"initDurationMs"` } -type MetricsContainer struct { - Metrics *model.Metrics `json:"metricset"` -} - -// Add adds a metric with the given name, labels, and value, -// The labels are expected to be sorted lexicographically. -func (mc MetricsContainer) Add(name string, value float64) { - mc.addMetric(name, model.Metric{Value: value}) -} - -// Simplified version of https://github.com/elastic/apm-agent-go/blob/675e8398c7fe546f9fd169bef971b9ccfbcdc71f/metrics.go#L89 -func (mc MetricsContainer) addMetric(name string, metric model.Metric) { - - if mc.Metrics.Samples == nil { - mc.Metrics.Samples = make(map[string]model.Metric) - } - mc.Metrics.Samples[name] = metric -} - -func (mc MetricsContainer) MarshalFastJSON(json *fastjson.Writer) error { - json.RawString(`{"metricset":`) - if err := mc.Metrics.MarshalFastJSON(json); err != nil { - return err - } - json.RawString(`}`) - return nil -} - // ProcessPlatformReport processes the `platform.report` log line from lambda logs API and // returns a byte array containing the JSON body for the extracted platform metrics. A non // nil error is returned when marshaling of platform metrics into JSON fails. func ProcessPlatformReport(fnARN string, deadlineMs int64, ts time.Time, platformReport LogEvent) ([]byte, error) { - metricsContainer := MetricsContainer{ + metricsContainer := model.MetricsContainer{ Metrics: &model.Metrics{}, } convMB2Bytes := float64(1024 * 1024) @@ -76,7 +48,7 @@ func ProcessPlatformReport(fnARN string, deadlineMs int64, ts time.Time, platfor metricsContainer.Metrics.Timestamp = model.Time(platformReport.Time) // FaaS Fields - metricsContainer.Metrics.FAAS = &model.FAAS{ + metricsContainer.Metrics.FAAS = &model.ExtendedFAAS{ Execution: platformReport.Record.RequestID, ID: fnARN, Coldstart: platformReportMetrics.InitDurationMs > 0, diff --git a/logsapi/model/model.go b/logsapi/model/model.go new file mode 100644 index 00000000..5d3f42de --- /dev/null +++ b/logsapi/model/model.go @@ -0,0 +1,89 @@ +// 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 model + +import ( + "time" + + "go.elastic.co/fastjson" +) + +type LogContainer struct { + Log *LogLine `json:"log,omitempty"` +} + +type LogLine struct { + Message string `json:"message"` + Timestamp Time `json:"@timestamp"` + FAAS *FAAS `json:"faas,omitempty"` +} + +type Time time.Time + +func (t Time) MarshalFastJSON(w *fastjson.Writer) error { + w.Int64(time.Time(t).UnixNano() / int64(time.Microsecond)) + return nil +} + +// faas struct is a subset of go.elastic.co/apm/v2/model#FAAS +// +// The purpose of having a separate struct is to have a custom +// marshalling logic that is targeted for the faas fields +// available for function logs. For example: `coldstart` value +// cannot be inferred for function logs so this struct drops +// the field entirely. +type FAAS struct { + // ID holds a unique identifier of the invoked serverless function. + ID string `json:"id,omitempty"` + // Execution holds the request ID of the function invocation. + Execution string `json:"execution,omitempty"` +} + +type MetricsContainer struct { + Metrics *Metrics `json:"metricset,omitempty"` +} + +// Add adds a metric with the given name, labels, and value, +// The labels are expected to be sorted lexicographically. +func (mc MetricsContainer) Add(name string, value float64) { + mc.addMetric(name, Metric{Value: value}) +} + +// Simplified version of https://github.com/elastic/apm-agent-go/blob/675e8398c7fe546f9fd169bef971b9ccfbcdc71f/metrics.go#L89 +func (mc MetricsContainer) addMetric(name string, metric Metric) { + if mc.Metrics.Samples == nil { + mc.Metrics.Samples = make(map[string]Metric) + } + mc.Metrics.Samples[name] = metric +} + +type Metrics struct { + Timestamp Time `json:"timestamp"` + FAAS *ExtendedFAAS `json:"faas,omitempty"` + Samples map[string]Metric `json:"samples,omitempty"` +} + +type ExtendedFAAS struct { + ID string `json:"id,omitempty"` + Execution string `json:"execution,omitempty"` + Coldstart bool `json:"coldstart"` +} + +type Metric struct { + Value float64 `json:"value"` +} diff --git a/logsapi/model/model_json.go b/logsapi/model/model_json.go new file mode 100644 index 00000000..354206e4 --- /dev/null +++ b/logsapi/model/model_json.go @@ -0,0 +1,157 @@ +// 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. + +// Code generated by "generate-fastjson". DO NOT EDIT. + +package model + +import ( + "go.elastic.co/fastjson" +) + +func (v *LogContainer) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + if v.Log != nil { + w.RawString("\"log\":") + if err := v.Log.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *LogLine) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"@timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + w.RawString(",\"message\":") + w.String(v.Message) + if v.FAAS != nil { + w.RawString(",\"faas\":") + if err := v.FAAS.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *FAAS) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + first := true + if v.Execution != "" { + const prefix = ",\"execution\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Execution) + } + if v.ID != "" { + const prefix = ",\"id\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.ID) + } + w.RawByte('}') + return nil +} + +func (v *MetricsContainer) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + if v.Metrics != nil { + w.RawString("\"metricset\":") + if err := v.Metrics.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + w.RawByte('}') + return firstErr +} + +func (v *Metrics) MarshalFastJSON(w *fastjson.Writer) error { + var firstErr error + w.RawByte('{') + w.RawString("\"timestamp\":") + if err := v.Timestamp.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + if v.FAAS != nil { + w.RawString(",\"faas\":") + if err := v.FAAS.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + if v.Samples != nil { + w.RawString(",\"samples\":") + w.RawByte('{') + { + first := true + for k, v := range v.Samples { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if err := v.MarshalFastJSON(w); err != nil && firstErr == nil { + firstErr = err + } + } + } + w.RawByte('}') + } + w.RawByte('}') + return firstErr +} + +func (v *ExtendedFAAS) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"coldstart\":") + w.Bool(v.Coldstart) + if v.Execution != "" { + w.RawString(",\"execution\":") + w.String(v.Execution) + } + if v.ID != "" { + w.RawString(",\"id\":") + w.String(v.ID) + } + w.RawByte('}') + return nil +} + +func (v *Metric) MarshalFastJSON(w *fastjson.Writer) error { + w.RawByte('{') + w.RawString("\"value\":") + w.Float64(v.Value) + w.RawByte('}') + return nil +}