lambda_extension/
telemetry.rs

1use chrono::{DateTime, Utc};
2use http::{Request, Response};
3use http_body_util::BodyExt;
4use hyper::body::Incoming;
5use lambda_runtime_api_client::body::Body;
6use serde::{Deserialize, Serialize};
7use std::{boxed::Box, fmt, sync::Arc};
8use tokio::sync::Mutex;
9use tower::Service;
10use tracing::{error, trace};
11
12/// Payload received from the Telemetry API
13#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
14pub struct LambdaTelemetry {
15    /// Time when the telemetry was generated
16    pub time: DateTime<Utc>,
17    /// Telemetry record entry
18    #[serde(flatten)]
19    pub record: LambdaTelemetryRecord,
20}
21
22/// Record in a LambdaTelemetry entry
23#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
24#[serde(tag = "type", content = "record", rename_all = "lowercase")]
25pub enum LambdaTelemetryRecord {
26    /// Function log records
27    Function(String),
28
29    /// Extension log records
30    Extension(String),
31
32    /// Platform init start record
33    #[serde(rename = "platform.initStart", rename_all = "camelCase")]
34    PlatformInitStart {
35        /// Type of initialization
36        initialization_type: InitType,
37        /// Phase of initialisation
38        phase: InitPhase,
39        /// Lambda runtime version
40        #[serde(skip_serializing_if = "Option::is_none")]
41        runtime_version: Option<String>,
42        /// Lambda runtime version ARN
43        #[serde(skip_serializing_if = "Option::is_none")]
44        runtime_version_arn: Option<String>,
45    },
46    /// Platform init runtime done record
47    #[serde(rename = "platform.initRuntimeDone", rename_all = "camelCase")]
48    PlatformInitRuntimeDone {
49        /// Type of initialization
50        initialization_type: InitType,
51        /// Phase of initialisation
52        #[serde(skip_serializing_if = "Option::is_none")]
53        phase: Option<InitPhase>,
54        /// Status of initalization
55        status: Status,
56        /// When the status = failure, the error_type describes what kind of error occurred
57        #[serde(skip_serializing_if = "Option::is_none")]
58        error_type: Option<String>,
59        /// Spans
60        #[serde(default)]
61        spans: Vec<Span>,
62    },
63    /// Platform init start record
64    #[serde(rename = "platform.initReport", rename_all = "camelCase")]
65    PlatformInitReport {
66        /// Type of initialization
67        initialization_type: InitType,
68        /// Phase of initialisation
69        phase: InitPhase,
70        /// Metrics
71        metrics: InitReportMetrics,
72        /// Spans
73        #[serde(default)]
74        spans: Vec<Span>,
75    },
76    /// Record marking start of an invocation
77    #[serde(rename = "platform.start", rename_all = "camelCase")]
78    PlatformStart {
79        /// Request identifier
80        request_id: String,
81        /// Version of the Lambda function
82        #[serde(skip_serializing_if = "Option::is_none")]
83        version: Option<String>,
84        /// Trace Context
85        #[serde(skip_serializing_if = "Option::is_none")]
86        tracing: Option<TraceContext>,
87    },
88    /// Record marking the completion of an invocation
89    #[serde(rename = "platform.runtimeDone", rename_all = "camelCase")]
90    PlatformRuntimeDone {
91        /// Request identifier
92        request_id: String,
93        /// Status of the invocation
94        status: Status,
95        /// When unsuccessful, the error_type describes what kind of error occurred
96        #[serde(skip_serializing_if = "Option::is_none")]
97        error_type: Option<String>,
98        /// Metrics corresponding to the runtime
99        #[serde(skip_serializing_if = "Option::is_none")]
100        metrics: Option<RuntimeDoneMetrics>,
101        /// Spans
102        #[serde(default)]
103        spans: Vec<Span>,
104        /// Trace Context
105        #[serde(skip_serializing_if = "Option::is_none")]
106        tracing: Option<TraceContext>,
107    },
108    /// Platfor report record
109    #[serde(rename = "platform.report", rename_all = "camelCase")]
110    PlatformReport {
111        /// Request identifier
112        request_id: String,
113        /// Status of the invocation
114        status: Status,
115        /// When unsuccessful, the error_type describes what kind of error occurred
116        #[serde(skip_serializing_if = "Option::is_none")]
117        error_type: Option<String>,
118        /// Metrics
119        metrics: ReportMetrics,
120        /// Spans
121        #[serde(default)]
122        spans: Vec<Span>,
123        /// Trace Context
124        #[serde(skip_serializing_if = "Option::is_none")]
125        tracing: Option<TraceContext>,
126    },
127
128    /// Extension-specific record
129    #[serde(rename = "platform.extension", rename_all = "camelCase")]
130    PlatformExtension {
131        /// Name of the extension
132        name: String,
133        /// State of the extension
134        state: String,
135        /// Events sent to the extension
136        events: Vec<String>,
137    },
138    /// Telemetry processor-specific record
139    #[serde(rename = "platform.telemetrySubscription", rename_all = "camelCase")]
140    PlatformTelemetrySubscription {
141        /// Name of the extension
142        name: String,
143        /// State of the extensions
144        state: String,
145        /// Types of records sent to the extension
146        types: Vec<String>,
147    },
148    /// Record generated when the telemetry processor is falling behind
149    #[serde(rename = "platform.logsDropped", rename_all = "camelCase")]
150    PlatformLogsDropped {
151        /// Reason for dropping the logs
152        reason: String,
153        /// Number of records dropped
154        dropped_records: u64,
155        /// Total size of the dropped records
156        dropped_bytes: u64,
157    },
158}
159
160/// Type of Initialization
161#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
162#[serde(rename_all = "kebab-case")]
163pub enum InitType {
164    /// Initialised on demand
165    OnDemand,
166    /// Initialized to meet the provisioned concurrency
167    ProvisionedConcurrency,
168    /// SnapStart
169    SnapStart,
170}
171
172/// Phase in which initialization occurs
173#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
174#[serde(rename_all = "kebab-case")]
175pub enum InitPhase {
176    /// Initialization phase
177    Init,
178    /// Invocation phase
179    Invoke,
180}
181
182/// Status of invocation/initialization
183#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
184#[serde(rename_all = "kebab-case")]
185pub enum Status {
186    /// Success
187    Success,
188    /// Error
189    Error,
190    /// Failure
191    Failure,
192    /// Timeout
193    Timeout,
194}
195
196/// Span
197#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
198#[serde(rename_all = "camelCase")]
199pub struct Span {
200    /// Duration of the span
201    pub duration_ms: f64,
202    /// Name of the span
203    pub name: String,
204    /// Start of the span
205    pub start: DateTime<Utc>,
206}
207
208/// Tracing Context
209#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
210#[serde(rename_all = "camelCase")]
211pub struct TraceContext {
212    /// Span ID
213    pub span_id: Option<String>,
214    /// Type of tracing
215    pub r#type: TracingType,
216    /// A string containing tracing information like trace_id. The contents may depend on the TracingType.
217    pub value: String,
218}
219
220/// Type of tracing
221#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
222pub enum TracingType {
223    /// Amazon trace type
224    #[serde(rename = "X-Amzn-Trace-Id")]
225    AmznTraceId,
226}
227
228///Init report metrics
229#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
230#[serde(rename_all = "camelCase")]
231pub struct InitReportMetrics {
232    /// Duration of initialization
233    pub duration_ms: f64,
234}
235
236/// Report metrics
237#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
238#[serde(rename_all = "camelCase")]
239pub struct ReportMetrics {
240    /// Duration in milliseconds
241    pub duration_ms: f64,
242    /// Billed duration in milliseconds
243    pub billed_duration_ms: u64,
244    /// Memory allocated in megabytes
245    #[serde(rename = "memorySizeMB")]
246    pub memory_size_mb: u64,
247    /// Maximum memory used for the invoke in megabytes
248    #[serde(rename = "maxMemoryUsedMB")]
249    pub max_memory_used_mb: u64,
250    /// Init duration in case of a cold start
251    #[serde(default = "Option::default", skip_serializing_if = "Option::is_none")]
252    pub init_duration_ms: Option<f64>,
253    /// Restore duration in milliseconds
254    #[serde(default = "Option::default", skip_serializing_if = "Option::is_none")]
255    pub restore_duration_ms: Option<f64>,
256}
257
258/// Runtime done metrics
259#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
260#[serde(rename_all = "camelCase")]
261pub struct RuntimeDoneMetrics {
262    /// Duration in milliseconds
263    pub duration_ms: f64,
264    /// Number of bytes produced as a result of the invocation
265    pub produced_bytes: Option<u64>,
266}
267
268/// Wrapper function that sends telemetry to the subscriber Service
269///
270/// This takes an `hyper::Request` and transforms it into `Vec<LambdaTelemetry>` for the
271/// underlying `Service` to process.
272pub(crate) async fn telemetry_wrapper<S>(
273    service: Arc<Mutex<S>>,
274    req: Request<Incoming>,
275) -> Result<Response<Body>, Box<dyn std::error::Error + Send + Sync>>
276where
277    S: Service<Vec<LambdaTelemetry>, Response = ()>,
278    S::Error: Into<Box<dyn std::error::Error + Send + Sync>> + fmt::Debug,
279    S::Future: Send,
280{
281    trace!("Received telemetry request");
282    // Parse the request body as a Vec<LambdaTelemetry>
283    let body = match req.into_body().collect().await {
284        Ok(body) => body,
285        Err(e) => {
286            error!("Error reading telemetry request body: {}", e);
287            return Ok(hyper::Response::builder()
288                .status(hyper::StatusCode::BAD_REQUEST)
289                .body(Body::empty())
290                .unwrap());
291        }
292    };
293
294    let telemetry: Vec<LambdaTelemetry> = match serde_json::from_slice(&body.to_bytes()) {
295        Ok(telemetry) => telemetry,
296        Err(e) => {
297            error!("Error parsing telemetry: {}", e);
298            return Ok(hyper::Response::builder()
299                .status(hyper::StatusCode::BAD_REQUEST)
300                .body(Body::empty())
301                .unwrap());
302        }
303    };
304
305    {
306        let mut service = service.lock().await;
307        match service.call(telemetry).await {
308            Ok(_) => (),
309            Err(err) => println!("{err:?}"),
310        }
311    }
312
313    Ok(hyper::Response::new(Body::empty()))
314}
315
316#[cfg(test)]
317mod deserialization_tests {
318    use super::*;
319    use chrono::{TimeDelta, TimeZone};
320
321    macro_rules! deserialize_tests {
322        ($($name:ident: $value:expr,)*) => {
323            $(
324                #[test]
325                fn $name() {
326                    let (input, expected) = $value;
327                    let actual = serde_json::from_str::<LambdaTelemetry>(&input).expect("unable to deserialize");
328
329                    assert!(actual.record == expected);
330                }
331            )*
332        }
333    }
334
335    deserialize_tests! {
336        // function
337        function: (
338            r#"{"time": "2020-08-20T12:31:32.123Z","type": "function", "record": "hello world"}"#,
339            LambdaTelemetryRecord::Function("hello world".to_string()),
340        ),
341
342        // extension
343        extension: (
344            r#"{"time": "2020-08-20T12:31:32.123Z","type": "extension", "record": "hello world"}"#,
345            LambdaTelemetryRecord::Extension("hello world".to_string()),
346        ),
347
348        // platform.start
349        platform_start: (
350            r#"{"time":"2022-10-21T14:05:03.165Z","type":"platform.start","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","version":"$LATEST","tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"}}}"#,
351            LambdaTelemetryRecord::PlatformStart {
352                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
353                version: Some("$LATEST".to_string()),
354                tracing: Some(TraceContext{
355                    span_id: Some("24cd7d670fa455f0".to_string()),
356                    r#type: TracingType::AmznTraceId,
357                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
358                }),
359            },
360        ),
361        // platform.initStart
362        platform_init_start: (
363            r#"{"time":"2022-10-19T13:52:15.636Z","type":"platform.initStart","record":{"initializationType":"on-demand","phase":"init"}}"#,
364            LambdaTelemetryRecord::PlatformInitStart {
365                initialization_type: InitType::OnDemand,
366                phase: InitPhase::Init,
367                runtime_version: None,
368                runtime_version_arn: None,
369            },
370        ),
371        // platform.runtimeDone
372        platform_runtime_done: (
373            r#"{"time":"2022-10-21T14:05:05.764Z","type":"platform.runtimeDone","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","status":"success","tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"},"spans":[{"name":"responseLatency","start":"2022-10-21T14:05:03.165Z","durationMs":2598.0},{"name":"responseDuration","start":"2022-10-21T14:05:05.763Z","durationMs":0.0}],"metrics":{"durationMs":2599.0,"producedBytes":8}}}"#,
374            LambdaTelemetryRecord::PlatformRuntimeDone {
375                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
376                status: Status::Success,
377                error_type: None,
378                metrics: Some(RuntimeDoneMetrics {
379                    duration_ms: 2599.0,
380                    produced_bytes: Some(8),
381                }),
382                spans: vec!(
383                    Span {
384                        name:"responseLatency".to_string(),
385                        start: Utc
386                            .with_ymd_and_hms(2022, 10, 21, 14, 5, 3)
387                            .unwrap()
388                            .checked_add_signed(TimeDelta::try_milliseconds(165).unwrap())
389                            .unwrap(),
390                        duration_ms: 2598.0
391                    },
392                    Span {
393                        name:"responseDuration".to_string(),
394                        start: Utc
395                            .with_ymd_and_hms(2022, 10, 21, 14, 5, 5)
396                            .unwrap()
397                            .checked_add_signed(TimeDelta::try_milliseconds(763).unwrap())
398                            .unwrap(),
399                        duration_ms: 0.0
400                    },
401                ),
402                tracing: Some(TraceContext{
403                    span_id: Some("24cd7d670fa455f0".to_string()),
404                    r#type: TracingType::AmznTraceId,
405                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
406                }),
407            },
408        ),
409        // platform.report
410        platform_report: (
411            r#"{"time":"2022-10-21T14:05:05.766Z","type":"platform.report","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","metrics":{"durationMs":2599.4,"billedDurationMs":2600,"memorySizeMB":128,"maxMemoryUsedMB":94,"initDurationMs":549.04},"tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"},"status":"success"}}"#,
412            LambdaTelemetryRecord::PlatformReport {
413                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
414                status: Status::Success,
415                error_type: None,
416                metrics: ReportMetrics {
417                    duration_ms: 2599.4,
418                    billed_duration_ms: 2600,
419                    memory_size_mb:128,
420                    max_memory_used_mb:94,
421                    init_duration_ms: Some(549.04),
422                    restore_duration_ms: None,
423                },
424                spans: Vec::new(),
425                tracing: Some(TraceContext {
426                    span_id: Some("24cd7d670fa455f0".to_string()),
427                    r#type: TracingType::AmznTraceId,
428                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
429                }),
430            },
431        ),
432        // platform.telemetrySubscription
433        platform_telemetry_subscription: (
434            r#"{"time":"2022-10-19T13:52:15.667Z","type":"platform.telemetrySubscription","record":{"name":"my-extension","state":"Subscribed","types":["platform","function"]}}"#,
435            LambdaTelemetryRecord::PlatformTelemetrySubscription {
436                 name: "my-extension".to_string(),
437                 state: "Subscribed".to_string(),
438                 types: vec!("platform".to_string(), "function".to_string()),
439            },
440        ),
441        // platform.initRuntimeDone
442        platform_init_runtime_done: (
443            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.initRuntimeDone","record":{"initializationType":"on-demand","status":"success"}}"#,
444            LambdaTelemetryRecord::PlatformInitRuntimeDone {
445                initialization_type: InitType::OnDemand,
446                status: Status::Success,
447                phase: None,
448                error_type: None,
449                spans: Vec::new(),
450            },
451        ),
452        // platform.extension
453        platform_extension: (
454            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.extension","record":{"name":"my-extension","state":"Ready","events":["SHUTDOWN","INVOKE"]}}"#,
455            LambdaTelemetryRecord::PlatformExtension {
456                name: "my-extension".to_string(),
457                state: "Ready".to_string(),
458                events: vec!("SHUTDOWN".to_string(), "INVOKE".to_string()),
459             },
460        ),
461        // platform.initReport
462        platform_init_report: (
463            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.initReport","record":{"initializationType":"on-demand","metrics":{"durationMs":500.0},"phase":"init"}}"#,
464            LambdaTelemetryRecord::PlatformInitReport {
465                initialization_type: InitType::OnDemand,
466                phase: InitPhase::Init,
467                metrics: InitReportMetrics { duration_ms: 500.0 },
468                spans: Vec::new(),
469            }
470        ),
471    }
472}
473
474#[cfg(test)]
475mod serialization_tests {
476    use chrono::{TimeDelta, TimeZone};
477
478    use super::*;
479    macro_rules! serialize_tests {
480        ($($name:ident: $value:expr,)*) => {
481            $(
482                #[test]
483                fn $name() {
484                    let (input, expected) = $value;
485                    let actual = serde_json::to_string(&input).expect("unable to serialize");
486                    println!("Input: {:?}\n", input);
487                    println!("Expected:\n {:?}\n", expected);
488                    println!("Actual:\n {:?}\n", actual);
489
490                    assert!(actual == expected);
491                }
492            )*
493        }
494    }
495
496    serialize_tests! {
497        // function
498        function: (
499            LambdaTelemetry {
500                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
501                record: LambdaTelemetryRecord::Function("hello world".to_string()),
502            },
503            r#"{"time":"2023-11-28T12:00:09Z","type":"function","record":"hello world"}"#,
504        ),
505        // extension
506        extension: (
507            LambdaTelemetry {
508                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
509                record: LambdaTelemetryRecord::Extension("hello world".to_string()),
510            },
511            r#"{"time":"2023-11-28T12:00:09Z","type":"extension","record":"hello world"}"#,
512        ),
513        //platform.Start
514        platform_start: (
515            LambdaTelemetry{
516                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
517                record: LambdaTelemetryRecord::PlatformStart {
518                        request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
519                        version: Some("$LATEST".to_string()),
520                        tracing: Some(TraceContext{
521                            span_id: Some("24cd7d670fa455f0".to_string()),
522                            r#type: TracingType::AmznTraceId,
523                            value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
524                    }),
525                }
526            },
527            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.start","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","version":"$LATEST","tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"}}}"#,
528        ),
529        // platform.initStart
530        platform_init_start: (
531            LambdaTelemetry{
532                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
533                record: LambdaTelemetryRecord::PlatformInitStart {
534                        initialization_type: InitType::OnDemand,
535                        phase: InitPhase::Init,
536                        runtime_version: None,
537                        runtime_version_arn: None,
538                },
539            },
540            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initStart","record":{"initializationType":"on-demand","phase":"init"}}"#,
541        ),
542        // platform.runtimeDone
543        platform_runtime_done: (
544            LambdaTelemetry{
545                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
546                record: LambdaTelemetryRecord::PlatformRuntimeDone {
547                    request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
548                    status: Status::Success,
549                    error_type: None,
550                    metrics: Some(RuntimeDoneMetrics {
551                        duration_ms: 2599.0,
552                        produced_bytes: Some(8),
553                    }),
554                    spans: vec!(
555                        Span {
556                            name:"responseLatency".to_string(),
557                            start: Utc
558                                .with_ymd_and_hms(2022, 10, 21, 14, 5, 3)
559                                .unwrap()
560                                .checked_add_signed(TimeDelta::try_milliseconds(165).unwrap())
561                                .unwrap(),
562                            duration_ms: 2598.0
563                        },
564                        Span {
565                            name:"responseDuration".to_string(),
566                            start: Utc
567                                .with_ymd_and_hms(2022, 10, 21, 14, 5, 5)
568                                .unwrap()
569                                .checked_add_signed(TimeDelta::try_milliseconds(763).unwrap())
570                                .unwrap(),
571                            duration_ms: 0.0
572                        },
573                    ),
574                    tracing: Some(TraceContext{
575                        span_id: Some("24cd7d670fa455f0".to_string()),
576                        r#type: TracingType::AmznTraceId,
577                        value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
578                    }),
579                },
580            },
581            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.runtimeDone","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","status":"success","metrics":{"durationMs":2599.0,"producedBytes":8},"spans":[{"durationMs":2598.0,"name":"responseLatency","start":"2022-10-21T14:05:03.165Z"},{"durationMs":0.0,"name":"responseDuration","start":"2022-10-21T14:05:05.763Z"}],"tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"}}}"#,
582        ),
583        // platform.report
584        platform_report: (
585            LambdaTelemetry{
586                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
587                record: LambdaTelemetryRecord::PlatformReport {
588                    request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
589                    status: Status::Success,
590                    error_type: None,
591                    metrics: ReportMetrics {
592                        duration_ms: 2599.4,
593                        billed_duration_ms: 2600,
594                        memory_size_mb:128,
595                        max_memory_used_mb:94,
596                        init_duration_ms: Some(549.04),
597                        restore_duration_ms: None,
598                    },
599                    spans: Vec::new(),
600                    tracing: Some(TraceContext {
601                        span_id: Some("24cd7d670fa455f0".to_string()),
602                        r#type: TracingType::AmznTraceId,
603                        value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
604                    }),
605                },
606            },
607            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.report","record":{"requestId":"459921b5-681c-4a96-beb0-81e0aa586026","status":"success","metrics":{"durationMs":2599.4,"billedDurationMs":2600,"memorySizeMB":128,"maxMemoryUsedMB":94,"initDurationMs":549.04},"spans":[],"tracing":{"spanId":"24cd7d670fa455f0","type":"X-Amzn-Trace-Id","value":"Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1"}}}"#,
608        ),
609        // platform.telemetrySubscription
610        platform_telemetry_subscription: (
611            LambdaTelemetry{
612                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
613                record: LambdaTelemetryRecord::PlatformTelemetrySubscription {
614                    name: "my-extension".to_string(),
615                    state: "Subscribed".to_string(),
616                    types: vec!("platform".to_string(), "function".to_string()),
617                },
618            },
619            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.telemetrySubscription","record":{"name":"my-extension","state":"Subscribed","types":["platform","function"]}}"#,
620        ),
621        // platform.initRuntimeDone
622        platform_init_runtime_done: (
623            LambdaTelemetry{
624                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
625                record: LambdaTelemetryRecord::PlatformInitRuntimeDone {
626                    initialization_type: InitType::OnDemand,
627                    status: Status::Success,
628                    phase: None,
629                    error_type: None,
630                    spans: Vec::new(),
631            },
632            },
633            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initRuntimeDone","record":{"initializationType":"on-demand","status":"success","spans":[]}}"#,
634        ),
635        // platform.extension
636        platform_extension: (
637            LambdaTelemetry {
638                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
639                record: LambdaTelemetryRecord::PlatformExtension {
640                    name: "my-extension".to_string(),
641                    state: "Ready".to_string(),
642                    events: vec!("SHUTDOWN".to_string(), "INVOKE".to_string()),
643                },
644            },
645            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.extension","record":{"name":"my-extension","state":"Ready","events":["SHUTDOWN","INVOKE"]}}"#,
646        ),
647        // platform.initReport
648        platform_init_report: (
649            LambdaTelemetry {
650                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
651                record: LambdaTelemetryRecord::PlatformInitReport {
652                    initialization_type: InitType::OnDemand,
653                    phase: InitPhase::Init,
654                    metrics: InitReportMetrics { duration_ms: 500.0 },
655                    spans: Vec::new(),
656                },
657            },
658            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initReport","record":{"initializationType":"on-demand","phase":"init","metrics":{"durationMs":500.0},"spans":[]}}"#,
659        ),
660
661    }
662}