1
//! Access logger support for dynamic modules.
2
//!
3
//! This module provides traits and types for implementing access loggers as dynamic modules.
4

            
5
use crate::{abi, EnvoyBuffer};
6
use std::ffi::c_void;
7
use std::ptr;
8

            
9
// -----------------------------------------------------------------------------
10
// Metrics Support
11
// -----------------------------------------------------------------------------
12

            
13
/// Handle for a counter metric. Counters can only be incremented.
14
#[derive(Debug, Clone, Copy)]
15
pub struct CounterHandle {
16
  id: usize,
17
}
18

            
19
/// Handle for a gauge metric. Gauges can be set, incremented, or decremented.
20
#[derive(Debug, Clone, Copy)]
21
pub struct GaugeHandle {
22
  id: usize,
23
}
24

            
25
/// Handle for a histogram metric. Histograms record value distributions.
26
#[derive(Debug, Clone, Copy)]
27
pub struct HistogramHandle {
28
  id: usize,
29
}
30

            
31
/// Provides access to metrics operations during configuration.
32
///
33
/// This is passed to `AccessLoggerConfig::new` to allow defining metrics.
34
pub struct ConfigContext {
35
  envoy_ptr: *mut c_void,
36
}
37

            
38
impl ConfigContext {
39
  /// Create a new ConfigContext. Used internally by the macro.
40
  #[doc(hidden)]
41
  pub fn new(envoy_ptr: *mut c_void) -> Self {
42
    Self { envoy_ptr }
43
  }
44

            
45
  /// Define a counter metric.
46
  ///
47
  /// Counters are cumulative metrics that can only increase. They are reset on restart.
48
  /// Returns a handle that can be used to increment the counter.
49
  pub fn define_counter(&self, name: &str) -> Option<CounterHandle> {
50
    let name_buf = abi::envoy_dynamic_module_type_module_buffer {
51
      ptr: name.as_ptr() as *const _,
52
      length: name.len(),
53
    };
54
    let mut id: usize = 0;
55
    let result = unsafe {
56
      abi::envoy_dynamic_module_callback_access_logger_config_define_counter(
57
        self.envoy_ptr,
58
        name_buf,
59
        &mut id,
60
      )
61
    };
62
    if result == abi::envoy_dynamic_module_type_metrics_result::Success {
63
      Some(CounterHandle { id })
64
    } else {
65
      None
66
    }
67
  }
68

            
69
  /// Define a gauge metric.
70
  ///
71
  /// Gauges are metrics that can go up and down. They represent a current value.
72
  /// Returns a handle that can be used to set/increment/decrement the gauge.
73
  pub fn define_gauge(&self, name: &str) -> Option<GaugeHandle> {
74
    let name_buf = abi::envoy_dynamic_module_type_module_buffer {
75
      ptr: name.as_ptr() as *const _,
76
      length: name.len(),
77
    };
78
    let mut id: usize = 0;
79
    let result = unsafe {
80
      abi::envoy_dynamic_module_callback_access_logger_config_define_gauge(
81
        self.envoy_ptr,
82
        name_buf,
83
        &mut id,
84
      )
85
    };
86
    if result == abi::envoy_dynamic_module_type_metrics_result::Success {
87
      Some(GaugeHandle { id })
88
    } else {
89
      None
90
    }
91
  }
92

            
93
  /// Define a histogram metric.
94
  ///
95
  /// Histograms track the distribution of values. They are useful for measuring latencies.
96
  /// Returns a handle that can be used to record values in the histogram.
97
  pub fn define_histogram(&self, name: &str) -> Option<HistogramHandle> {
98
    let name_buf = abi::envoy_dynamic_module_type_module_buffer {
99
      ptr: name.as_ptr() as *const _,
100
      length: name.len(),
101
    };
102
    let mut id: usize = 0;
103
    let result = unsafe {
104
      abi::envoy_dynamic_module_callback_access_logger_config_define_histogram(
105
        self.envoy_ptr,
106
        name_buf,
107
        &mut id,
108
      )
109
    };
110
    if result == abi::envoy_dynamic_module_type_metrics_result::Success {
111
      Some(HistogramHandle { id })
112
    } else {
113
      None
114
    }
115
  }
116

            
117
  /// Get the raw Envoy pointer. Used internally.
118
  #[doc(hidden)]
119
  pub fn envoy_ptr(&self) -> *mut c_void {
120
    self.envoy_ptr
121
  }
122
}
123

            
124
/// Provides access to metrics operations at runtime.
125
///
126
/// This is stored in the logger and used to update metric values during log events.
127
pub struct MetricsContext {
128
  envoy_ptr: *mut c_void,
129
}
130

            
131
// SAFETY: The envoy_ptr points to Envoy's DynamicModuleAccessLogConfig which is thread-safe.
132
// The metrics callbacks are designed to be called from any thread.
133
unsafe impl Send for MetricsContext {}
134

            
135
impl MetricsContext {
136
  /// Create a new MetricsContext. Used internally by the macro.
137
  #[doc(hidden)]
138
  pub fn new(envoy_ptr: *mut c_void) -> Self {
139
    Self { envoy_ptr }
140
  }
141

            
142
  /// Increment a counter by the given value.
143
  pub fn increment_counter(&self, handle: CounterHandle, value: u64) -> bool {
144
    let result = unsafe {
145
      abi::envoy_dynamic_module_callback_access_logger_increment_counter(
146
        self.envoy_ptr,
147
        handle.id,
148
        value,
149
      )
150
    };
151
    result == abi::envoy_dynamic_module_type_metrics_result::Success
152
  }
153

            
154
  /// Set a gauge to the given value.
155
  pub fn set_gauge(&self, handle: GaugeHandle, value: u64) -> bool {
156
    let result = unsafe {
157
      abi::envoy_dynamic_module_callback_access_logger_set_gauge(self.envoy_ptr, handle.id, value)
158
    };
159
    result == abi::envoy_dynamic_module_type_metrics_result::Success
160
  }
161

            
162
  /// Increment a gauge by the given value.
163
  pub fn increment_gauge(&self, handle: GaugeHandle, value: u64) -> bool {
164
    let result = unsafe {
165
      abi::envoy_dynamic_module_callback_access_logger_increment_gauge(
166
        self.envoy_ptr,
167
        handle.id,
168
        value,
169
      )
170
    };
171
    result == abi::envoy_dynamic_module_type_metrics_result::Success
172
  }
173

            
174
  /// Decrement a gauge by the given value.
175
  pub fn decrement_gauge(&self, handle: GaugeHandle, value: u64) -> bool {
176
    let result = unsafe {
177
      abi::envoy_dynamic_module_callback_access_logger_decrement_gauge(
178
        self.envoy_ptr,
179
        handle.id,
180
        value,
181
      )
182
    };
183
    result == abi::envoy_dynamic_module_type_metrics_result::Success
184
  }
185

            
186
  /// Record a value in a histogram.
187
  pub fn record_histogram(&self, handle: HistogramHandle, value: u64) -> bool {
188
    let result = unsafe {
189
      abi::envoy_dynamic_module_callback_access_logger_record_histogram_value(
190
        self.envoy_ptr,
191
        handle.id,
192
        value,
193
      )
194
    };
195
    result == abi::envoy_dynamic_module_type_metrics_result::Success
196
  }
197
}
198

            
199
/// Trait that the dynamic module must implement to provide the access logger configuration.
200
pub trait AccessLoggerConfig: Sized + Send + Sync + 'static {
201
  /// Create a new configuration from the provided name and config bytes.
202
  ///
203
  /// The `ctx` provides access to metrics definition APIs. Metrics should be defined
204
  /// during configuration creation and the handles stored in the config for later use.
205
  fn new(ctx: &ConfigContext, name: &str, config: &[u8]) -> Result<Self, String>;
206

            
207
  /// Create a logger instance. Called per-thread for thread-local loggers.
208
  ///
209
  /// The `metrics` context is stored and can be used to update metrics during log events.
210
  fn create_logger(
211
    &self,
212
    metrics: MetricsContext,
213
    logger_envoy_ptr: *mut ::std::ffi::c_void,
214
  ) -> Box<dyn AccessLogger>;
215
}
216

            
217
/// Logger trait that handles individual log events.
218
pub trait AccessLogger: Send {
219
  /// Called when a log event occurs.
220
  fn log(&mut self, ctx: &LogContext);
221

            
222
  /// Called to flush buffered logs before the logger is destroyed.
223
  ///
224
  /// This is called during shutdown when the thread-local logger is being destroyed.
225
  /// Modules that buffer log entries should implement this to ensure no logs are lost.
226
  ///
227
  /// This is optional. The default implementation does nothing.
228
  fn flush(&mut self) {}
229
}
230

            
231
/// Timing information from the stream info.
232
#[derive(Debug, Clone, Default)]
233
pub struct TimingInfo {
234
  /// Request start time as Unix timestamp in nanoseconds.
235
  pub start_time_unix_ns: i64,
236
  /// Duration from start to request complete in nanoseconds, or -1 if not available.
237
  pub request_complete_duration_ns: i64,
238
  /// Time of first upstream TX byte sent in nanoseconds, or -1 if not available.
239
  pub first_upstream_tx_byte_sent_ns: i64,
240
  /// Time of last upstream TX byte sent in nanoseconds, or -1 if not available.
241
  pub last_upstream_tx_byte_sent_ns: i64,
242
  /// Time of first upstream RX byte received in nanoseconds, or -1 if not available.
243
  pub first_upstream_rx_byte_received_ns: i64,
244
  /// Time of last upstream RX byte received in nanoseconds, or -1 if not available.
245
  pub last_upstream_rx_byte_received_ns: i64,
246
  /// Time of first downstream TX byte sent in nanoseconds, or -1 if not available.
247
  pub first_downstream_tx_byte_sent_ns: i64,
248
  /// Time of last downstream TX byte sent in nanoseconds, or -1 if not available.
249
  pub last_downstream_tx_byte_sent_ns: i64,
250
}
251

            
252
/// Byte count information from the stream info.
253
#[derive(Debug, Clone, Default)]
254
pub struct BytesInfo {
255
  /// Total bytes received from downstream.
256
  pub bytes_received: u64,
257
  /// Total bytes sent to downstream.
258
  pub bytes_sent: u64,
259
  /// Wire bytes received (including TLS overhead).
260
  pub wire_bytes_received: u64,
261
  /// Wire bytes sent (including TLS overhead).
262
  pub wire_bytes_sent: u64,
263
}
264

            
265
/// Access log type indicating when the log was recorded.
266
///
267
/// This corresponds to `envoy::data::accesslog::v3::AccessLogType`.
268
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269
#[repr(u32)]
270
pub enum AccessLogType {
271
  NotSet               = 0,
272
  TcpUpstreamConnected = 1,
273
  TcpPeriodic          = 2,
274
  TcpConnectionEnd     = 3,
275
  DownstreamStart      = 4,
276
  DownstreamPeriodic   = 5,
277
  DownstreamEnd        = 6,
278
  UpstreamPoolReady    = 7,
279
  UpstreamPeriodic     = 8,
280
  UpstreamEnd          = 9,
281
  DownstreamTunnelSuccessfullyEstablished = 10,
282
  UdpTunnelUpstreamConnected = 11,
283
  UdpPeriodic          = 12,
284
  UdpSessionEnd        = 13,
285
}
286

            
287
impl AccessLogType {
288
  /// Convert from the ABI enum value. Used internally by the macro.
289
  #[doc(hidden)]
290
  pub fn from_abi(value: abi::envoy_dynamic_module_type_access_log_type) -> Self {
291
    match value {
292
      abi::envoy_dynamic_module_type_access_log_type::TcpUpstreamConnected => {
293
        AccessLogType::TcpUpstreamConnected
294
      },
295
      abi::envoy_dynamic_module_type_access_log_type::TcpPeriodic => AccessLogType::TcpPeriodic,
296
      abi::envoy_dynamic_module_type_access_log_type::TcpConnectionEnd => {
297
        AccessLogType::TcpConnectionEnd
298
      },
299
      abi::envoy_dynamic_module_type_access_log_type::DownstreamStart => {
300
        AccessLogType::DownstreamStart
301
      },
302
      abi::envoy_dynamic_module_type_access_log_type::DownstreamPeriodic => {
303
        AccessLogType::DownstreamPeriodic
304
      },
305
      abi::envoy_dynamic_module_type_access_log_type::DownstreamEnd => AccessLogType::DownstreamEnd,
306
      abi::envoy_dynamic_module_type_access_log_type::UpstreamPoolReady => {
307
        AccessLogType::UpstreamPoolReady
308
      },
309
      abi::envoy_dynamic_module_type_access_log_type::UpstreamPeriodic => {
310
        AccessLogType::UpstreamPeriodic
311
      },
312
      abi::envoy_dynamic_module_type_access_log_type::UpstreamEnd => AccessLogType::UpstreamEnd,
313
      abi::envoy_dynamic_module_type_access_log_type::DownstreamTunnelSuccessfullyEstablished => {
314
        AccessLogType::DownstreamTunnelSuccessfullyEstablished
315
      },
316
      abi::envoy_dynamic_module_type_access_log_type::UdpTunnelUpstreamConnected => {
317
        AccessLogType::UdpTunnelUpstreamConnected
318
      },
319
      abi::envoy_dynamic_module_type_access_log_type::UdpPeriodic => AccessLogType::UdpPeriodic,
320
      abi::envoy_dynamic_module_type_access_log_type::UdpSessionEnd => AccessLogType::UdpSessionEnd,
321
      _ => AccessLogType::NotSet,
322
    }
323
  }
324

            
325
  /// Get the string representation matching Envoy's `AccessLogType_Name`.
326
  pub fn as_str(&self) -> &'static str {
327
    match self {
328
      AccessLogType::NotSet => "NotSet",
329
      AccessLogType::TcpUpstreamConnected => "TcpUpstreamConnected",
330
      AccessLogType::TcpPeriodic => "TcpPeriodic",
331
      AccessLogType::TcpConnectionEnd => "TcpConnectionEnd",
332
      AccessLogType::DownstreamStart => "DownstreamStart",
333
      AccessLogType::DownstreamPeriodic => "DownstreamPeriodic",
334
      AccessLogType::DownstreamEnd => "DownstreamEnd",
335
      AccessLogType::UpstreamPoolReady => "UpstreamPoolReady",
336
      AccessLogType::UpstreamPeriodic => "UpstreamPeriodic",
337
      AccessLogType::UpstreamEnd => "UpstreamEnd",
338
      AccessLogType::DownstreamTunnelSuccessfullyEstablished => {
339
        "DownstreamTunnelSuccessfullyEstablished"
340
      },
341
      AccessLogType::UdpTunnelUpstreamConnected => "UdpTunnelUpstreamConnected",
342
      AccessLogType::UdpPeriodic => "UdpPeriodic",
343
      AccessLogType::UdpSessionEnd => "UdpSessionEnd",
344
    }
345
  }
346
}
347

            
348
/// Read-only context for accessing log event data.
349
pub struct LogContext {
350
  // Private field - only accessible within this crate.
351
  pub(crate) envoy_ptr: *mut c_void,
352
  /// The type of access log event.
353
  pub(crate) log_type: AccessLogType,
354
}
355

            
356
impl LogContext {
357
  /// Create a new LogContext. Used internally by the macro.
358
  #[doc(hidden)]
359
  pub fn new(envoy_ptr: *mut c_void, log_type: AccessLogType) -> Self {
360
    Self {
361
      envoy_ptr,
362
      log_type,
363
    }
364
  }
365

            
366
  /// Get the access log type indicating when this log event was recorded.
367
  pub fn log_type(&self) -> AccessLogType {
368
    self.log_type
369
  }
370

            
371
  /// Get the HTTP response code.
372
  pub fn response_code(&self) -> Option<u32> {
373
    let code =
374
      unsafe { abi::envoy_dynamic_module_callback_access_logger_get_response_code(self.envoy_ptr) };
375

            
376
    if code != 0 {
377
      Some(code)
378
    } else {
379
      None
380
    }
381
  }
382

            
383
  /// Get the response code details string.
384
  pub fn response_code_details(&self) -> Option<EnvoyBuffer<'_>> {
385
    self
386
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_response_code_details)
387
  }
388

            
389
  /// Get the request protocol (e.g., "HTTP/1.1", "HTTP/2").
390
  pub fn protocol(&self) -> Option<EnvoyBuffer<'_>> {
391
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_protocol)
392
  }
393

            
394
  /// Get timing information.
395
  pub fn timing_info(&self) -> TimingInfo {
396
    let mut info = abi::envoy_dynamic_module_type_timing_info {
397
      start_time_unix_ns: 0,
398
      request_complete_duration_ns: -1,
399
      first_upstream_tx_byte_sent_ns: -1,
400
      last_upstream_tx_byte_sent_ns: -1,
401
      first_upstream_rx_byte_received_ns: -1,
402
      last_upstream_rx_byte_received_ns: -1,
403
      first_downstream_tx_byte_sent_ns: -1,
404
      last_downstream_tx_byte_sent_ns: -1,
405
    };
406
    unsafe {
407
      abi::envoy_dynamic_module_callback_access_logger_get_timing_info(self.envoy_ptr, &mut info);
408
    }
409
    TimingInfo {
410
      start_time_unix_ns: info.start_time_unix_ns,
411
      request_complete_duration_ns: info.request_complete_duration_ns,
412
      first_upstream_tx_byte_sent_ns: info.first_upstream_tx_byte_sent_ns,
413
      last_upstream_tx_byte_sent_ns: info.last_upstream_tx_byte_sent_ns,
414
      first_upstream_rx_byte_received_ns: info.first_upstream_rx_byte_received_ns,
415
      last_upstream_rx_byte_received_ns: info.last_upstream_rx_byte_received_ns,
416
      first_downstream_tx_byte_sent_ns: info.first_downstream_tx_byte_sent_ns,
417
      last_downstream_tx_byte_sent_ns: info.last_downstream_tx_byte_sent_ns,
418
    }
419
  }
420

            
421
  /// Get byte count information.
422
  pub fn bytes_info(&self) -> BytesInfo {
423
    let mut info = abi::envoy_dynamic_module_type_bytes_info {
424
      bytes_received: 0,
425
      bytes_sent: 0,
426
      wire_bytes_received: 0,
427
      wire_bytes_sent: 0,
428
    };
429
    unsafe {
430
      abi::envoy_dynamic_module_callback_access_logger_get_bytes_info(self.envoy_ptr, &mut info);
431
    }
432
    BytesInfo {
433
      bytes_received: info.bytes_received,
434
      bytes_sent: info.bytes_sent,
435
      wire_bytes_received: info.wire_bytes_received,
436
      wire_bytes_sent: info.wire_bytes_sent,
437
    }
438
  }
439

            
440
  /// Get the route name.
441
  pub fn route_name(&self) -> Option<EnvoyBuffer<'_>> {
442
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_route_name)
443
  }
444

            
445
  /// Get the virtual cluster name.
446
  pub fn virtual_cluster_name(&self) -> Option<EnvoyBuffer<'_>> {
447
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_virtual_cluster_name)
448
  }
449

            
450
  /// Check if this is a health check request.
451
  pub fn is_health_check(&self) -> bool {
452
    unsafe { abi::envoy_dynamic_module_callback_access_logger_is_health_check(self.envoy_ptr) }
453
  }
454

            
455
  /// Get the upstream cluster name.
456
  pub fn upstream_cluster(&self) -> Option<EnvoyBuffer<'_>> {
457
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_cluster)
458
  }
459

            
460
  /// Get the upstream host hostname.
461
  pub fn upstream_host(&self) -> Option<EnvoyBuffer<'_>> {
462
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_host)
463
  }
464

            
465
  /// Get the connection ID, or 0 if not available.
466
  pub fn connection_id(&self) -> u64 {
467
    unsafe { abi::envoy_dynamic_module_callback_access_logger_get_connection_id(self.envoy_ptr) }
468
  }
469

            
470
  /// Check if mTLS was used for the connection.
471
  pub fn is_mtls(&self) -> bool {
472
    unsafe { abi::envoy_dynamic_module_callback_access_logger_is_mtls(self.envoy_ptr) }
473
  }
474

            
475
  /// Get the requested server name (SNI).
476
  pub fn requested_server_name(&self) -> Option<EnvoyBuffer<'_>> {
477
    self
478
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_requested_server_name)
479
  }
480

            
481
  /// Check if the request was sampled for tracing.
482
  pub fn is_trace_sampled(&self) -> bool {
483
    unsafe { abi::envoy_dynamic_module_callback_access_logger_is_trace_sampled(self.envoy_ptr) }
484
  }
485

            
486
  /// Get a request header value by key.
487
  ///
488
  /// For headers with multiple values, use `index` to access subsequent values.
489
  /// Returns the total count of values in `total_count` if provided.
490
  pub fn get_request_header(&self, key: &str) -> Option<EnvoyBuffer<'_>> {
491
    self.get_header_value(
492
      abi::envoy_dynamic_module_type_http_header_type::RequestHeader,
493
      key,
494
      0,
495
    )
496
  }
497

            
498
  /// Get a response header value by key.
499
  pub fn get_response_header(&self, key: &str) -> Option<EnvoyBuffer<'_>> {
500
    self.get_header_value(
501
      abi::envoy_dynamic_module_type_http_header_type::ResponseHeader,
502
      key,
503
      0,
504
    )
505
  }
506

            
507
  /// Get a header value by type and key.
508
  fn get_header_value(
509
    &self,
510
    header_type: abi::envoy_dynamic_module_type_http_header_type,
511
    key: &str,
512
    index: usize,
513
  ) -> Option<EnvoyBuffer<'_>> {
514
    let key_buf = abi::envoy_dynamic_module_type_module_buffer {
515
      ptr: key.as_ptr() as *const _,
516
      length: key.len(),
517
    };
518
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
519
      ptr: ptr::null_mut(),
520
      length: 0,
521
    };
522
    if unsafe {
523
      abi::envoy_dynamic_module_callback_access_logger_get_header_value(
524
        self.envoy_ptr,
525
        header_type,
526
        key_buf,
527
        &mut result,
528
        index,
529
        ptr::null_mut(),
530
      )
531
    } {
532
      Some(unsafe { EnvoyBuffer::new_from_raw(result.ptr as *const u8, result.length) })
533
    } else {
534
      None
535
    }
536
  }
537

            
538
  /// Get a value from dynamic metadata.
539
  ///
540
  /// # Arguments
541
  /// * `filter_name` - The filter namespace (e.g., "envoy.filters.http.dynamic_module").
542
  /// * `key` - The key within the filter namespace (e.g., "rbac_policy").
543
  ///
544
  /// # Returns
545
  /// The string value if it exists, None otherwise.
546
  /// Note: Only string values are currently supported.
547
  pub fn get_dynamic_metadata(&self, filter_name: &str, key: &str) -> Option<EnvoyBuffer<'_>> {
548
    let filter_buf = abi::envoy_dynamic_module_type_module_buffer {
549
      ptr: filter_name.as_ptr() as *const _,
550
      length: filter_name.len(),
551
    };
552
    let key_buf = abi::envoy_dynamic_module_type_module_buffer {
553
      ptr: key.as_ptr() as *const _,
554
      length: key.len(),
555
    };
556
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
557
      ptr: ptr::null_mut(),
558
      length: 0,
559
    };
560
    if unsafe {
561
      abi::envoy_dynamic_module_callback_access_logger_get_dynamic_metadata(
562
        self.envoy_ptr,
563
        filter_buf,
564
        key_buf,
565
        &mut result,
566
      )
567
    } {
568
      Some(unsafe { EnvoyBuffer::new_from_raw(result.ptr as *const u8, result.length) })
569
    } else {
570
      None
571
    }
572
  }
573

            
574
  /// Get the local reply body (if this was a local response).
575
  pub fn local_reply_body(&self) -> Option<EnvoyBuffer<'_>> {
576
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_local_reply_body)
577
  }
578

            
579
  /// Get the index of the current worker thread.
580
  pub fn get_worker_index(&self) -> u32 {
581
    unsafe { abi::envoy_dynamic_module_callback_access_logger_get_worker_index(self.envoy_ptr) }
582
  }
583

            
584
  /// Check if a specific response flag is set.
585
  ///
586
  /// Response flags indicate various error conditions or special processing that occurred
587
  /// during request handling (e.g., upstream connection failure, rate limiting).
588
  pub fn has_response_flag(&self, flag: abi::envoy_dynamic_module_type_response_flag) -> bool {
589
    unsafe {
590
      abi::envoy_dynamic_module_callback_access_logger_has_response_flag(self.envoy_ptr, flag)
591
    }
592
  }
593

            
594
  /// Get all response flags as a bitmask.
595
  ///
596
  /// Each bit corresponds to a response flag value. Use bitwise operations to check
597
  /// individual flags, or use [`has_response_flag`](Self::has_response_flag) for single flag
598
  /// checks.
599
  pub fn response_flags(&self) -> u64 {
600
    unsafe { abi::envoy_dynamic_module_callback_access_logger_get_response_flags(self.envoy_ptr) }
601
  }
602

            
603
  /// Get the upstream request attempt count, or 0 if not available.
604
  pub fn attempt_count(&self) -> u32 {
605
    unsafe { abi::envoy_dynamic_module_callback_access_logger_get_attempt_count(self.envoy_ptr) }
606
  }
607

            
608
  /// Get the connection termination details.
609
  pub fn connection_termination_details(&self) -> Option<EnvoyBuffer<'_>> {
610
    self.get_envoy_buffer(
611
      abi::envoy_dynamic_module_callback_access_logger_get_connection_termination_details,
612
    )
613
  }
614

            
615
  /// Get the downstream remote address (client) as an IP address buffer and port.
616
  ///
617
  /// Returns `None` if the address is not available or is not an IP address.
618
  pub fn downstream_remote_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
619
    self.get_address(abi::envoy_dynamic_module_callback_access_logger_get_downstream_remote_address)
620
  }
621

            
622
  /// Get the downstream local address (Envoy listener) as an IP address buffer and port.
623
  ///
624
  /// Returns `None` if the address is not available or is not an IP address.
625
  pub fn downstream_local_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
626
    self.get_address(abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_address)
627
  }
628

            
629
  /// Get the upstream remote address (backend) as an IP address buffer and port.
630
  ///
631
  /// Returns `None` if the address is not available or is not an IP address.
632
  pub fn upstream_remote_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
633
    self.get_address(abi::envoy_dynamic_module_callback_access_logger_get_upstream_remote_address)
634
  }
635

            
636
  /// Get the upstream local address (Envoy outbound) as an IP address buffer and port.
637
  ///
638
  /// Returns `None` if the address is not available or is not an IP address.
639
  pub fn upstream_local_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
640
    self.get_address(abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_address)
641
  }
642

            
643
  /// Get the downstream direct remote address (physical peer address before XFF processing).
644
  ///
645
  /// Returns `None` if the address is not available or is not an IP address.
646
  pub fn downstream_direct_remote_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
647
    self.get_address(
648
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_direct_remote_address,
649
    )
650
  }
651

            
652
  /// Get the downstream direct local address (physical listener address).
653
  ///
654
  /// Returns `None` if the address is not available or is not an IP address.
655
  pub fn downstream_direct_local_address(&self) -> Option<(EnvoyBuffer<'_>, u32)> {
656
    self.get_address(
657
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_direct_local_address,
658
    )
659
  }
660

            
661
  /// Helper to retrieve an address (IP buffer + port) from an ABI callback.
662
  fn get_address(
663
    &self,
664
    callback: unsafe extern "C" fn(
665
      *mut c_void,
666
      *mut abi::envoy_dynamic_module_type_envoy_buffer,
667
      *mut u32,
668
    ) -> bool,
669
  ) -> Option<(EnvoyBuffer<'_>, u32)> {
670
    let mut address = abi::envoy_dynamic_module_type_envoy_buffer {
671
      ptr: ptr::null_mut(),
672
      length: 0,
673
    };
674
    let mut port: u32 = 0;
675
    if unsafe { callback(self.envoy_ptr, &mut address, &mut port) } {
676
      Some((
677
        unsafe { EnvoyBuffer::new_from_raw(address.ptr as *const u8, address.length) },
678
        port,
679
      ))
680
    } else {
681
      None
682
    }
683
  }
684

            
685
  /// Get the upstream transport failure reason.
686
  pub fn upstream_transport_failure_reason(&self) -> Option<EnvoyBuffer<'_>> {
687
    self.get_envoy_buffer(
688
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_transport_failure_reason,
689
    )
690
  }
691

            
692
  /// Get the JA3 fingerprint hash from the downstream connection.
693
  pub fn ja3_hash(&self) -> Option<EnvoyBuffer<'_>> {
694
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_ja3_hash)
695
  }
696

            
697
  /// Get the JA4 fingerprint hash from the downstream connection.
698
  pub fn ja4_hash(&self) -> Option<EnvoyBuffer<'_>> {
699
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_ja4_hash)
700
  }
701

            
702
  /// Get the downstream transport failure reason.
703
  pub fn downstream_transport_failure_reason(&self) -> Option<EnvoyBuffer<'_>> {
704
    self.get_envoy_buffer(
705
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_transport_failure_reason,
706
    )
707
  }
708

            
709
  /// Get the byte size of request headers (uncompressed).
710
  pub fn request_headers_bytes(&self) -> u64 {
711
    unsafe {
712
      abi::envoy_dynamic_module_callback_access_logger_get_request_headers_bytes(self.envoy_ptr)
713
    }
714
  }
715

            
716
  /// Get the byte size of response headers (uncompressed).
717
  pub fn response_headers_bytes(&self) -> u64 {
718
    unsafe {
719
      abi::envoy_dynamic_module_callback_access_logger_get_response_headers_bytes(self.envoy_ptr)
720
    }
721
  }
722

            
723
  /// Get the byte size of response trailers (uncompressed).
724
  pub fn response_trailers_bytes(&self) -> u64 {
725
    unsafe {
726
      abi::envoy_dynamic_module_callback_access_logger_get_response_trailers_bytes(self.envoy_ptr)
727
    }
728
  }
729

            
730
  /// Get the upstream protocol (e.g., "HTTP/1.1", "HTTP/2").
731
  pub fn upstream_protocol(&self) -> Option<EnvoyBuffer<'_>> {
732
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_protocol)
733
  }
734

            
735
  /// Get the upstream connection pool ready duration in nanoseconds, or -1 if not available.
736
  pub fn upstream_connection_pool_ready_duration_ns(&self) -> i64 {
737
    unsafe {
738
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_pool_ready_duration_ns(
739
        self.envoy_ptr,
740
      )
741
    }
742
  }
743

            
744
  /// Get the downstream TLS version (e.g., "TLSv1.2", "TLSv1.3").
745
  pub fn downstream_tls_version(&self) -> Option<EnvoyBuffer<'_>> {
746
    self
747
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_downstream_tls_version)
748
  }
749

            
750
  /// Get the downstream peer certificate subject (e.g., "CN=client").
751
  pub fn downstream_peer_subject(&self) -> Option<EnvoyBuffer<'_>> {
752
    self.get_envoy_buffer(
753
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_subject,
754
    )
755
  }
756

            
757
  /// Get the downstream peer certificate SHA-256 digest.
758
  pub fn downstream_peer_cert_digest(&self) -> Option<EnvoyBuffer<'_>> {
759
    self.get_envoy_buffer(
760
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_cert_digest,
761
    )
762
  }
763

            
764
  /// Get the downstream TLS cipher suite.
765
  ///
766
  /// The returned buffer uses thread-local storage and is valid until the next call to
767
  /// this method or [`upstream_tls_cipher`](Self::upstream_tls_cipher) on the same thread.
768
  pub fn downstream_tls_cipher(&self) -> Option<EnvoyBuffer<'_>> {
769
    self
770
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_downstream_tls_cipher)
771
  }
772

            
773
  /// Get the downstream TLS session ID.
774
  pub fn downstream_tls_session_id(&self) -> Option<EnvoyBuffer<'_>> {
775
    self.get_envoy_buffer(
776
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_tls_session_id,
777
    )
778
  }
779

            
780
  /// Get the downstream peer certificate issuer.
781
  pub fn downstream_peer_issuer(&self) -> Option<EnvoyBuffer<'_>> {
782
    self
783
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_issuer)
784
  }
785

            
786
  /// Get the downstream peer certificate serial number.
787
  pub fn downstream_peer_serial(&self) -> Option<EnvoyBuffer<'_>> {
788
    self
789
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_serial)
790
  }
791

            
792
  /// Get the downstream peer certificate SHA-1 fingerprint.
793
  pub fn downstream_peer_fingerprint_1(&self) -> Option<EnvoyBuffer<'_>> {
794
    self.get_envoy_buffer(
795
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_fingerprint_1,
796
    )
797
  }
798

            
799
  /// Get the downstream local certificate subject (Envoy's own certificate).
800
  pub fn downstream_local_subject(&self) -> Option<EnvoyBuffer<'_>> {
801
    self.get_envoy_buffer(
802
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_subject,
803
    )
804
  }
805

            
806
  /// Check if the downstream peer certificate was presented.
807
  pub fn downstream_peer_cert_presented(&self) -> bool {
808
    unsafe {
809
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_cert_presented(
810
        self.envoy_ptr,
811
      )
812
    }
813
  }
814

            
815
  /// Check if the downstream peer certificate was validated.
816
  pub fn downstream_peer_cert_validated(&self) -> bool {
817
    unsafe {
818
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_cert_validated(
819
        self.envoy_ptr,
820
      )
821
    }
822
  }
823

            
824
  /// Get the downstream peer certificate validity start time as epoch seconds.
825
  ///
826
  /// Returns 0 if the certificate or validity time is not available.
827
  pub fn downstream_peer_cert_v_start(&self) -> i64 {
828
    unsafe {
829
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_cert_v_start(
830
        self.envoy_ptr,
831
      )
832
    }
833
  }
834

            
835
  /// Get the downstream peer certificate validity end time as epoch seconds.
836
  ///
837
  /// Returns 0 if the certificate or validity time is not available.
838
  pub fn downstream_peer_cert_v_end(&self) -> i64 {
839
    unsafe {
840
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_cert_v_end(
841
        self.envoy_ptr,
842
      )
843
    }
844
  }
845

            
846
  /// Get the URI Subject Alternative Names from the downstream peer certificate.
847
  pub fn downstream_peer_uri_san(&self) -> Vec<EnvoyBuffer<'_>> {
848
    self.get_san_list(
849
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_uri_san_size,
850
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_uri_san,
851
    )
852
  }
853

            
854
  /// Get the URI Subject Alternative Names from the downstream local certificate.
855
  pub fn downstream_local_uri_san(&self) -> Vec<EnvoyBuffer<'_>> {
856
    self.get_san_list(
857
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_uri_san_size,
858
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_uri_san,
859
    )
860
  }
861

            
862
  /// Get the DNS Subject Alternative Names from the downstream peer certificate.
863
  pub fn downstream_peer_dns_san(&self) -> Vec<EnvoyBuffer<'_>> {
864
    self.get_san_list(
865
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_dns_san_size,
866
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_peer_dns_san,
867
    )
868
  }
869

            
870
  /// Get the DNS Subject Alternative Names from the downstream local certificate.
871
  pub fn downstream_local_dns_san(&self) -> Vec<EnvoyBuffer<'_>> {
872
    self.get_san_list(
873
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_dns_san_size,
874
      abi::envoy_dynamic_module_callback_access_logger_get_downstream_local_dns_san,
875
    )
876
  }
877

            
878
  /// Get the upstream connection ID, or 0 if not available.
879
  pub fn upstream_connection_id(&self) -> u64 {
880
    unsafe {
881
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_connection_id(self.envoy_ptr)
882
    }
883
  }
884

            
885
  /// Get the upstream TLS version (e.g., "TLSv1.2", "TLSv1.3").
886
  pub fn upstream_tls_version(&self) -> Option<EnvoyBuffer<'_>> {
887
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_tls_version)
888
  }
889

            
890
  /// Get the upstream TLS cipher suite.
891
  ///
892
  /// The returned buffer uses thread-local storage and is valid until the next call to
893
  /// this method or [`downstream_tls_cipher`](Self::downstream_tls_cipher) on the same thread.
894
  pub fn upstream_tls_cipher(&self) -> Option<EnvoyBuffer<'_>> {
895
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_tls_cipher)
896
  }
897

            
898
  /// Get the upstream TLS session ID.
899
  pub fn upstream_tls_session_id(&self) -> Option<EnvoyBuffer<'_>> {
900
    self.get_envoy_buffer(
901
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_tls_session_id,
902
    )
903
  }
904

            
905
  /// Get the upstream peer certificate subject.
906
  pub fn upstream_peer_subject(&self) -> Option<EnvoyBuffer<'_>> {
907
    self
908
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_subject)
909
  }
910

            
911
  /// Get the upstream peer certificate issuer.
912
  pub fn upstream_peer_issuer(&self) -> Option<EnvoyBuffer<'_>> {
913
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_issuer)
914
  }
915

            
916
  /// Get the upstream local certificate subject (Envoy's own certificate for the upstream
917
  /// connection).
918
  pub fn upstream_local_subject(&self) -> Option<EnvoyBuffer<'_>> {
919
    self
920
      .get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_subject)
921
  }
922

            
923
  /// Get the upstream peer certificate SHA-256 digest.
924
  pub fn upstream_peer_cert_digest(&self) -> Option<EnvoyBuffer<'_>> {
925
    self.get_envoy_buffer(
926
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_cert_digest,
927
    )
928
  }
929

            
930
  /// Get the upstream peer certificate validity start time as epoch seconds.
931
  ///
932
  /// Returns 0 if the certificate or validity time is not available.
933
  pub fn upstream_peer_cert_v_start(&self) -> i64 {
934
    unsafe {
935
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_cert_v_start(
936
        self.envoy_ptr,
937
      )
938
    }
939
  }
940

            
941
  /// Get the upstream peer certificate validity end time as epoch seconds.
942
  ///
943
  /// Returns 0 if the certificate or validity time is not available.
944
  pub fn upstream_peer_cert_v_end(&self) -> i64 {
945
    unsafe {
946
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_cert_v_end(self.envoy_ptr)
947
    }
948
  }
949

            
950
  /// Get the URI Subject Alternative Names from the upstream peer certificate.
951
  pub fn upstream_peer_uri_san(&self) -> Vec<EnvoyBuffer<'_>> {
952
    self.get_san_list(
953
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_uri_san_size,
954
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_uri_san,
955
    )
956
  }
957

            
958
  /// Get the URI Subject Alternative Names from the upstream local certificate.
959
  pub fn upstream_local_uri_san(&self) -> Vec<EnvoyBuffer<'_>> {
960
    self.get_san_list(
961
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_uri_san_size,
962
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_uri_san,
963
    )
964
  }
965

            
966
  /// Get the DNS Subject Alternative Names from the upstream peer certificate.
967
  pub fn upstream_peer_dns_san(&self) -> Vec<EnvoyBuffer<'_>> {
968
    self.get_san_list(
969
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_dns_san_size,
970
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_peer_dns_san,
971
    )
972
  }
973

            
974
  /// Get the DNS Subject Alternative Names from the upstream local certificate.
975
  pub fn upstream_local_dns_san(&self) -> Vec<EnvoyBuffer<'_>> {
976
    self.get_san_list(
977
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_dns_san_size,
978
      abi::envoy_dynamic_module_callback_access_logger_get_upstream_local_dns_san,
979
    )
980
  }
981

            
982
  /// Get the request ID (stream ID).
983
  pub fn request_id(&self) -> Option<EnvoyBuffer<'_>> {
984
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_request_id)
985
  }
986

            
987
  /// Get a response trailer value by key.
988
  pub fn get_response_trailer(&self, key: &str) -> Option<EnvoyBuffer<'_>> {
989
    self.get_header_value(
990
      abi::envoy_dynamic_module_type_http_header_type::ResponseTrailer,
991
      key,
992
      0,
993
    )
994
  }
995

            
996
  /// Get a value from the filter state.
997
  ///
998
  /// Note: This is not currently supported and always returns `None`.
999
  /// Filter state serialization requires allocation which is incompatible
  /// with the zero-copy ABI design.
  pub fn get_filter_state(&self, key: &str) -> Option<EnvoyBuffer<'_>> {
    let key_buf = abi::envoy_dynamic_module_type_module_buffer {
      ptr: key.as_ptr() as *const _,
      length: key.len(),
    };
    let mut result = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: ptr::null_mut(),
      length: 0,
    };
    if unsafe {
      abi::envoy_dynamic_module_callback_access_logger_get_filter_state(
        self.envoy_ptr,
        key_buf,
        &mut result,
      )
    } {
      Some(unsafe { EnvoyBuffer::new_from_raw(result.ptr as *const u8, result.length) })
    } else {
      None
    }
  }
  /// Get the trace ID.
  ///
  /// Note: This is not currently supported and always returns `None`.
  /// The tracing span interface does not expose trace IDs in a way that
  /// allows zero-copy access.
  pub fn get_trace_id(&self) -> Option<EnvoyBuffer<'_>> {
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_trace_id)
  }
  /// Get the span ID.
  ///
  /// Note: This is not currently supported and always returns `None`.
  /// The tracing span interface does not expose span IDs in a way that
  /// allows zero-copy access.
  pub fn get_span_id(&self) -> Option<EnvoyBuffer<'_>> {
    self.get_envoy_buffer(abi::envoy_dynamic_module_callback_access_logger_get_span_id)
  }
  /// Get the number of headers of the specified type.
  ///
  /// The supported header types are `RequestHeader`, `ResponseHeader`, and `ResponseTrailer`.
  pub fn get_headers_count(
    &self,
    header_type: abi::envoy_dynamic_module_type_http_header_type,
  ) -> usize {
    unsafe {
      abi::envoy_dynamic_module_callback_access_logger_get_headers_size(self.envoy_ptr, header_type)
    }
  }
  /// Get all headers of the specified type as key-value `EnvoyBuffer` pairs.
  ///
  /// The supported header types are `RequestHeader`, `ResponseHeader`, and `ResponseTrailer`.
  /// Returns an empty vector if the header map is not available.
  pub fn get_all_headers(
    &self,
    header_type: abi::envoy_dynamic_module_type_http_header_type,
  ) -> Vec<(EnvoyBuffer<'_>, EnvoyBuffer<'_>)> {
    let count = self.get_headers_count(header_type);
    if count == 0 {
      return Vec::new();
    }
    let mut headers = vec![
      abi::envoy_dynamic_module_type_envoy_http_header {
        key_ptr: ptr::null_mut(),
        key_length: 0,
        value_ptr: ptr::null_mut(),
        value_length: 0,
      };
      count
    ];
    let success = unsafe {
      abi::envoy_dynamic_module_callback_access_logger_get_headers(
        self.envoy_ptr,
        header_type,
        headers.as_mut_ptr(),
      )
    };
    if !success {
      return Vec::new();
    }
    headers
      .iter()
      .map(|h| unsafe {
        (
          EnvoyBuffer::new_from_raw(h.key_ptr as *const u8, h.key_length),
          EnvoyBuffer::new_from_raw(h.value_ptr as *const u8, h.value_length),
        )
      })
      .collect()
  }
  /// Helper to retrieve an `EnvoyBuffer` from an ABI callback.
  fn get_envoy_buffer(
    &self,
    callback: unsafe extern "C" fn(
      *mut c_void,
      *mut abi::envoy_dynamic_module_type_envoy_buffer,
    ) -> bool,
  ) -> Option<EnvoyBuffer<'_>> {
    let mut buffer = abi::envoy_dynamic_module_type_envoy_buffer {
      ptr: ptr::null_mut(),
      length: 0,
    };
    if unsafe { callback(self.envoy_ptr, &mut buffer) } {
      Some(unsafe { EnvoyBuffer::new_from_raw(buffer.ptr as *const u8, buffer.length) })
    } else {
      None
    }
  }
  /// Helper to retrieve a list of SAN buffers from size + data ABI callbacks.
  fn get_san_list(
    &self,
    size_cb: unsafe extern "C" fn(*mut c_void) -> usize,
    data_cb: unsafe extern "C" fn(
      *mut c_void,
      *mut abi::envoy_dynamic_module_type_envoy_buffer,
    ) -> bool,
  ) -> Vec<EnvoyBuffer<'_>> {
    let count = unsafe { size_cb(self.envoy_ptr) };
    if count == 0 {
      return Vec::new();
    }
    let mut buffers = vec![
      abi::envoy_dynamic_module_type_envoy_buffer {
        ptr: ptr::null_mut(),
        length: 0,
      };
      count
    ];
    if !unsafe { data_cb(self.envoy_ptr, buffers.as_mut_ptr()) } {
      return Vec::new();
    }
    buffers
      .iter()
      .filter_map(|buf| {
        if buf.ptr.is_null() || buf.length == 0 {
          None
        } else {
          Some(unsafe { EnvoyBuffer::new_from_raw(buf.ptr as *const u8, buf.length) })
        }
      })
      .collect()
  }
}
/// Macro to declare access logger entry points.
///
/// This macro generates the required C ABI functions that Envoy calls to interact with
/// the access logger implementation.
///
/// # Example
///
/// ```ignore
/// use envoy_dynamic_modules_rust_sdk::{access_log::*, declare_access_logger};
///
/// struct MyLoggerConfig {
///     format: String,
///     logs_counter: CounterHandle,
///     config_envoy_ptr: *mut std::ffi::c_void,
/// }
///
/// unsafe impl Send for MyLoggerConfig {}
/// unsafe impl Sync for MyLoggerConfig {}
///
/// impl AccessLoggerConfig for MyLoggerConfig {
///     fn new(ctx: &ConfigContext, name: &str, config: &[u8]) -> Result<Self, String> {
///         let logs_counter = ctx.define_counter("logs_total")
///             .ok_or("Failed to define counter")?;
///         Ok(Self {
///             format: String::from_utf8_lossy(config).to_string(),
///             logs_counter,
///             config_envoy_ptr: ctx.envoy_ptr(),
///         })
///     }
///
///     fn create_logger(&self, metrics: MetricsContext) -> Box<dyn AccessLogger> {
///         Box::new(MyLogger {
///             format: self.format.clone(),
///             logs_counter: self.logs_counter,
///             metrics,
///         })
///     }
/// }
///
/// struct MyLogger {
///     format: String,
///     logs_counter: CounterHandle,
///     metrics: MetricsContext,
/// }
///
/// impl AccessLogger for MyLogger {
///     fn log(&mut self, ctx: &LogContext) {
///         self.metrics.increment_counter(self.logs_counter, 1);
///         if let Some(code) = ctx.response_code() {
///             println!("Response: {}", code);
///         }
///     }
/// }
///
/// declare_access_logger!(MyLoggerConfig);
/// ```
#[macro_export]
macro_rules! declare_access_logger {
  ($config_type:ty) => {
    /// Wrapper that stores both the config and the envoy pointer for metrics access.
    struct AccessLoggerConfigWrapper {
      config: $config_type,
      config_envoy_ptr: *mut ::std::ffi::c_void,
    }
    unsafe impl Send for AccessLoggerConfigWrapper {}
    unsafe impl Sync for AccessLoggerConfigWrapper {}
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_config_new(
      config_envoy_ptr: *mut ::std::ffi::c_void,
      name: $crate::abi::envoy_dynamic_module_type_envoy_buffer,
      config: $crate::abi::envoy_dynamic_module_type_envoy_buffer,
    ) -> *const ::std::ffi::c_void {
      let name_str = unsafe {
        let slice = ::std::slice::from_raw_parts(name.ptr as *const u8, name.length);
        ::std::str::from_utf8(slice).unwrap_or("")
      };
      let config_bytes =
        unsafe { ::std::slice::from_raw_parts(config.ptr as *const u8, config.length) };
      let ctx = $crate::access_log::ConfigContext::new(config_envoy_ptr);
      match <$config_type as $crate::access_log::AccessLoggerConfig>::new(
        &ctx,
        name_str,
        config_bytes,
      ) {
        Ok(c) => {
          let wrapper = AccessLoggerConfigWrapper {
            config: c,
            config_envoy_ptr,
          };
          Box::into_raw(Box::new(wrapper)) as *const ::std::ffi::c_void
        },
        Err(_) => ::std::ptr::null(),
      }
    }
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_config_destroy(
      config_ptr: *const ::std::ffi::c_void,
    ) {
      unsafe {
        drop(Box::from_raw(config_ptr as *mut AccessLoggerConfigWrapper));
      }
    }
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_new(
      config_ptr: *const ::std::ffi::c_void,
      logger_envoy_ptr: *mut ::std::ffi::c_void,
    ) -> *const ::std::ffi::c_void {
      let wrapper = unsafe { &*(config_ptr as *const AccessLoggerConfigWrapper) };
      let metrics = $crate::access_log::MetricsContext::new(wrapper.config_envoy_ptr);
      let logger = wrapper.config.create_logger(metrics, logger_envoy_ptr);
      Box::into_raw(Box::new(logger)) as *const ::std::ffi::c_void
    }
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_log(
      envoy_ptr: *mut ::std::ffi::c_void,
      logger_ptr: *mut ::std::ffi::c_void,
      log_type: $crate::abi::envoy_dynamic_module_type_access_log_type,
    ) {
      let logger = unsafe { &mut *(logger_ptr as *mut Box<dyn $crate::access_log::AccessLogger>) };
      let access_log_type = $crate::access_log::AccessLogType::from_abi(log_type);
      let ctx = $crate::access_log::LogContext::new(envoy_ptr, access_log_type);
      logger.log(&ctx);
    }
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_destroy(
      logger_ptr: *mut ::std::ffi::c_void,
    ) {
      unsafe {
        drop(Box::from_raw(
          logger_ptr as *mut Box<dyn $crate::access_log::AccessLogger>,
        ));
      }
    }
    #[no_mangle]
    pub extern "C" fn envoy_dynamic_module_on_access_logger_flush(
      logger_ptr: *mut ::std::ffi::c_void,
    ) {
      let logger = unsafe { &mut *(logger_ptr as *mut Box<dyn $crate::access_log::AccessLogger>) };
      logger.flush();
    }
  };
}