Skip to main content

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::{de::DeserializeOwned, 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<L = String> {
15    /// Time when the telemetry was generated
16    pub time: DateTime<Utc>,
17    /// Telemetry record entry
18    #[serde(flatten)]
19    pub record: LambdaTelemetryRecord<L>,
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<L = String> {
26    /// Function log records
27    Function(L),
28
29    /// Extension log records
30    Extension(L),
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, L>(
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<L>>, Response = ()>,
278    S::Error: Into<Box<dyn std::error::Error + Send + Sync>> + fmt::Debug,
279    S::Future: Send,
280    L: DeserializeOwned,
281{
282    trace!("Received telemetry request");
283    // Parse the request body as a Vec<LambdaTelemetry>
284    let body = match req.into_body().collect().await {
285        Ok(body) => body,
286        Err(e) => {
287            error!("Error reading telemetry request body: {}", e);
288            return Ok(hyper::Response::builder()
289                .status(hyper::StatusCode::BAD_REQUEST)
290                .body(Body::empty())
291                .unwrap());
292        }
293    };
294
295    let telemetry: Vec<LambdaTelemetry<L>> = match serde_json::from_slice(&body.to_bytes()) {
296        Ok(telemetry) => telemetry,
297        Err(e) => {
298            error!("Error parsing telemetry: {}", e);
299            return Ok(hyper::Response::builder()
300                .status(hyper::StatusCode::BAD_REQUEST)
301                .body(Body::empty())
302                .unwrap());
303        }
304    };
305
306    {
307        let mut service = service.lock().await;
308        match service.call(telemetry).await {
309            Ok(_) => (),
310            Err(err) => println!("{err:?}"),
311        }
312    }
313
314    Ok(hyper::Response::new(Body::empty()))
315}
316
317#[cfg(test)]
318mod deserialization_tests {
319    use super::*;
320    use chrono::{TimeDelta, TimeZone};
321
322    macro_rules! deserialize_tests {
323        ($($name:ident$(<$log:ty>)?: $value:expr,)*) => {
324            $(
325                #[test]
326                fn $name() {
327                    let (input, expected) = $value;
328                    let actual = serde_json::from_str::<LambdaTelemetry$(<$log>)?>(&input).expect("unable to deserialize");
329
330                    assert!(actual.record == expected);
331                }
332            )*
333        };
334    }
335
336    deserialize_tests! {
337        // function
338        function: (
339            r#"{"time": "2020-08-20T12:31:32.123Z","type": "function", "record": "hello world"}"#,
340            LambdaTelemetryRecord::Function("hello world".to_string()),
341        ),
342
343        // function (json)
344        function_generic<bool>: (
345            r#"{"time": "2020-08-20T12:31:32.123Z","type": "function", "record": true}"#,
346            LambdaTelemetryRecord::Function(true),
347        ),
348
349        // extension
350        extension: (
351            r#"{"time": "2020-08-20T12:31:32.123Z","type": "extension", "record": "hello world"}"#,
352            LambdaTelemetryRecord::Extension("hello world".to_string()),
353        ),
354
355        // extension (json)
356        extension_generic<bool>: (
357            r#"{"time": "2020-08-20T12:31:32.123Z","type": "extension", "record": true}"#,
358            LambdaTelemetryRecord::Extension(true),
359        ),
360
361        // platform.start
362        platform_start: (
363            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"}}}"#,
364            LambdaTelemetryRecord::PlatformStart {
365                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
366                version: Some("$LATEST".to_string()),
367                tracing: Some(TraceContext{
368                    span_id: Some("24cd7d670fa455f0".to_string()),
369                    r#type: TracingType::AmznTraceId,
370                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
371                }),
372            },
373        ),
374        // platform.initStart
375        platform_init_start: (
376            r#"{"time":"2022-10-19T13:52:15.636Z","type":"platform.initStart","record":{"initializationType":"on-demand","phase":"init"}}"#,
377            LambdaTelemetryRecord::PlatformInitStart {
378                initialization_type: InitType::OnDemand,
379                phase: InitPhase::Init,
380                runtime_version: None,
381                runtime_version_arn: None,
382            },
383        ),
384        // platform.runtimeDone
385        platform_runtime_done: (
386            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}}}"#,
387            LambdaTelemetryRecord::PlatformRuntimeDone {
388                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
389                status: Status::Success,
390                error_type: None,
391                metrics: Some(RuntimeDoneMetrics {
392                    duration_ms: 2599.0,
393                    produced_bytes: Some(8),
394                }),
395                spans: vec!(
396                    Span {
397                        name:"responseLatency".to_string(),
398                        start: Utc
399                            .with_ymd_and_hms(2022, 10, 21, 14, 5, 3)
400                            .unwrap()
401                            .checked_add_signed(TimeDelta::try_milliseconds(165).unwrap())
402                            .unwrap(),
403                        duration_ms: 2598.0
404                    },
405                    Span {
406                        name:"responseDuration".to_string(),
407                        start: Utc
408                            .with_ymd_and_hms(2022, 10, 21, 14, 5, 5)
409                            .unwrap()
410                            .checked_add_signed(TimeDelta::try_milliseconds(763).unwrap())
411                            .unwrap(),
412                        duration_ms: 0.0
413                    },
414                ),
415                tracing: Some(TraceContext{
416                    span_id: Some("24cd7d670fa455f0".to_string()),
417                    r#type: TracingType::AmznTraceId,
418                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
419                }),
420            },
421        ),
422        // platform.report
423        platform_report: (
424            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"}}"#,
425            LambdaTelemetryRecord::PlatformReport {
426                request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
427                status: Status::Success,
428                error_type: None,
429                metrics: ReportMetrics {
430                    duration_ms: 2599.4,
431                    billed_duration_ms: 2600,
432                    memory_size_mb:128,
433                    max_memory_used_mb:94,
434                    init_duration_ms: Some(549.04),
435                    restore_duration_ms: None,
436                },
437                spans: Vec::new(),
438                tracing: Some(TraceContext {
439                    span_id: Some("24cd7d670fa455f0".to_string()),
440                    r#type: TracingType::AmznTraceId,
441                    value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
442                }),
443            },
444        ),
445        // platform.telemetrySubscription
446        platform_telemetry_subscription: (
447            r#"{"time":"2022-10-19T13:52:15.667Z","type":"platform.telemetrySubscription","record":{"name":"my-extension","state":"Subscribed","types":["platform","function"]}}"#,
448            LambdaTelemetryRecord::PlatformTelemetrySubscription {
449                 name: "my-extension".to_string(),
450                 state: "Subscribed".to_string(),
451                 types: vec!("platform".to_string(), "function".to_string()),
452            },
453        ),
454        // platform.initRuntimeDone
455        platform_init_runtime_done: (
456            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.initRuntimeDone","record":{"initializationType":"on-demand","status":"success"}}"#,
457            LambdaTelemetryRecord::PlatformInitRuntimeDone {
458                initialization_type: InitType::OnDemand,
459                status: Status::Success,
460                phase: None,
461                error_type: None,
462                spans: Vec::new(),
463            },
464        ),
465        // platform.extension
466        platform_extension: (
467            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.extension","record":{"name":"my-extension","state":"Ready","events":["SHUTDOWN","INVOKE"]}}"#,
468            LambdaTelemetryRecord::PlatformExtension {
469                name: "my-extension".to_string(),
470                state: "Ready".to_string(),
471                events: vec!("SHUTDOWN".to_string(), "INVOKE".to_string()),
472             },
473        ),
474        // platform.initReport
475        platform_init_report: (
476            r#"{"time":"2022-10-19T13:52:16.136Z","type":"platform.initReport","record":{"initializationType":"on-demand","metrics":{"durationMs":500.0},"phase":"init"}}"#,
477            LambdaTelemetryRecord::PlatformInitReport {
478                initialization_type: InitType::OnDemand,
479                phase: InitPhase::Init,
480                metrics: InitReportMetrics { duration_ms: 500.0 },
481                spans: Vec::new(),
482            }
483        ),
484    }
485}
486
487#[cfg(test)]
488mod serialization_tests {
489    use chrono::{TimeDelta, TimeZone};
490
491    use super::*;
492    macro_rules! serialize_tests {
493        ($($name:ident$(<$log:ty>)?: $value:expr,)*) => {
494            $(
495                #[test]
496                fn $name() {
497                    let (input, expected): (LambdaTelemetry$(<$log>)?, &str) = $value;
498                    let actual = serde_json::to_string(&input).expect("unable to serialize");
499                    println!("Input: {:?}\n", input);
500                    println!("Expected:\n {:?}\n", expected);
501                    println!("Actual:\n {:?}\n", actual);
502
503                    assert!(actual == expected);
504                }
505            )*
506        };
507    }
508
509    serialize_tests! {
510        // function
511        function: (
512            LambdaTelemetry {
513                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
514                record: LambdaTelemetryRecord::Function("hello world".to_string()),
515            },
516            r#"{"time":"2023-11-28T12:00:09Z","type":"function","record":"hello world"}"#,
517        ),
518        // function (json)
519        function_generic<bool>: (
520            LambdaTelemetry {
521                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
522                record: LambdaTelemetryRecord::Function(true),
523            },
524            r#"{"time":"2023-11-28T12:00:09Z","type":"function","record":true}"#,
525        ),
526        // extension
527        extension: (
528            LambdaTelemetry {
529                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
530                record: LambdaTelemetryRecord::Extension("hello world".to_string()),
531            },
532            r#"{"time":"2023-11-28T12:00:09Z","type":"extension","record":"hello world"}"#,
533        ),
534        // extension (json)
535        extension_generic<bool>: (
536            LambdaTelemetry {
537                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
538                record: LambdaTelemetryRecord::Extension(true),
539            },
540            r#"{"time":"2023-11-28T12:00:09Z","type":"extension","record":true}"#,
541        ),
542        //platform.Start
543        platform_start: (
544            LambdaTelemetry{
545                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
546                record: LambdaTelemetryRecord::PlatformStart {
547                        request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
548                        version: Some("$LATEST".to_string()),
549                        tracing: Some(TraceContext{
550                            span_id: Some("24cd7d670fa455f0".to_string()),
551                            r#type: TracingType::AmznTraceId,
552                            value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
553                    }),
554                }
555            },
556            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"}}}"#,
557        ),
558        // platform.initStart
559        platform_init_start: (
560            LambdaTelemetry{
561                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
562                record: LambdaTelemetryRecord::PlatformInitStart {
563                        initialization_type: InitType::OnDemand,
564                        phase: InitPhase::Init,
565                        runtime_version: None,
566                        runtime_version_arn: None,
567                },
568            },
569            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initStart","record":{"initializationType":"on-demand","phase":"init"}}"#,
570        ),
571        // platform.runtimeDone
572        platform_runtime_done: (
573            LambdaTelemetry{
574                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
575                record: LambdaTelemetryRecord::PlatformRuntimeDone {
576                    request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
577                    status: Status::Success,
578                    error_type: None,
579                    metrics: Some(RuntimeDoneMetrics {
580                        duration_ms: 2599.0,
581                        produced_bytes: Some(8),
582                    }),
583                    spans: vec!(
584                        Span {
585                            name:"responseLatency".to_string(),
586                            start: Utc
587                                .with_ymd_and_hms(2022, 10, 21, 14, 5, 3)
588                                .unwrap()
589                                .checked_add_signed(TimeDelta::try_milliseconds(165).unwrap())
590                                .unwrap(),
591                            duration_ms: 2598.0
592                        },
593                        Span {
594                            name:"responseDuration".to_string(),
595                            start: Utc
596                                .with_ymd_and_hms(2022, 10, 21, 14, 5, 5)
597                                .unwrap()
598                                .checked_add_signed(TimeDelta::try_milliseconds(763).unwrap())
599                                .unwrap(),
600                            duration_ms: 0.0
601                        },
602                    ),
603                    tracing: Some(TraceContext{
604                        span_id: Some("24cd7d670fa455f0".to_string()),
605                        r#type: TracingType::AmznTraceId,
606                        value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
607                    }),
608                },
609            },
610            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"}}}"#,
611        ),
612        // platform.report
613        platform_report: (
614            LambdaTelemetry{
615                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
616                record: LambdaTelemetryRecord::PlatformReport {
617                    request_id: "459921b5-681c-4a96-beb0-81e0aa586026".to_string(),
618                    status: Status::Success,
619                    error_type: None,
620                    metrics: ReportMetrics {
621                        duration_ms: 2599.4,
622                        billed_duration_ms: 2600,
623                        memory_size_mb:128,
624                        max_memory_used_mb:94,
625                        init_duration_ms: Some(549.04),
626                        restore_duration_ms: None,
627                    },
628                    spans: Vec::new(),
629                    tracing: Some(TraceContext {
630                        span_id: Some("24cd7d670fa455f0".to_string()),
631                        r#type: TracingType::AmznTraceId,
632                        value: "Root=1-6352a70e-1e2c502e358361800241fd45;Parent=35465b3a9e2f7c6a;Sampled=1".to_string(),
633                    }),
634                },
635            },
636            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"}}}"#,
637        ),
638        // platform.telemetrySubscription
639        platform_telemetry_subscription: (
640            LambdaTelemetry{
641                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
642                record: LambdaTelemetryRecord::PlatformTelemetrySubscription {
643                    name: "my-extension".to_string(),
644                    state: "Subscribed".to_string(),
645                    types: vec!("platform".to_string(), "function".to_string()),
646                },
647            },
648            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.telemetrySubscription","record":{"name":"my-extension","state":"Subscribed","types":["platform","function"]}}"#,
649        ),
650        // platform.initRuntimeDone
651        platform_init_runtime_done: (
652            LambdaTelemetry{
653                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
654                record: LambdaTelemetryRecord::PlatformInitRuntimeDone {
655                    initialization_type: InitType::OnDemand,
656                    status: Status::Success,
657                    phase: None,
658                    error_type: None,
659                    spans: Vec::new(),
660            },
661            },
662            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initRuntimeDone","record":{"initializationType":"on-demand","status":"success","spans":[]}}"#,
663        ),
664        // platform.extension
665        platform_extension: (
666            LambdaTelemetry {
667                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
668                record: LambdaTelemetryRecord::PlatformExtension {
669                    name: "my-extension".to_string(),
670                    state: "Ready".to_string(),
671                    events: vec!("SHUTDOWN".to_string(), "INVOKE".to_string()),
672                },
673            },
674            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.extension","record":{"name":"my-extension","state":"Ready","events":["SHUTDOWN","INVOKE"]}}"#,
675        ),
676        // platform.initReport
677        platform_init_report: (
678            LambdaTelemetry {
679                time: Utc.with_ymd_and_hms(2023, 11, 28, 12, 0, 9).unwrap(),
680                record: LambdaTelemetryRecord::PlatformInitReport {
681                    initialization_type: InitType::OnDemand,
682                    phase: InitPhase::Init,
683                    metrics: InitReportMetrics { duration_ms: 500.0 },
684                    spans: Vec::new(),
685                },
686            },
687            r#"{"time":"2023-11-28T12:00:09Z","type":"platform.initReport","record":{"initializationType":"on-demand","phase":"init","metrics":{"durationMs":500.0},"spans":[]}}"#,
688        ),
689
690    }
691}