1#[cfg(unix)]
2use std::path::PathBuf;
3use std::{net::SocketAddr, num::NonZeroU64, time::Duration};
4
5use bytes::Bytes;
6use chrono::Utc;
7use futures::StreamExt;
8use listenfd::ListenFd;
9use smallvec::SmallVec;
10use tokio_util::udp::UdpFramed;
11use vector_lib::{
12 EstimatedJsonEncodedSizeOf,
13 codecs::{
14 BytesDecoder, OctetCountingDecoder, SyslogDeserializerConfig,
15 decoding::{Deserializer, Framer},
16 },
17 config::{LegacyKey, LogNamespace},
18 configurable::configurable_component,
19 internal_event::{ByteSize, BytesReceived, InternalEventHandle as _, Protocol},
20 ipallowlist::IpAllowlistConfig,
21 lookup::{OwnedValuePath, lookup_v2::OptionalValuePath, path},
22};
23use vrl::event_path;
24
25#[cfg(unix)]
26use crate::sources::util::build_unix_stream_source;
27use crate::{
28 SourceSender,
29 codecs::Decoder,
30 config::{
31 DataType, GenerateConfig, Resource, SourceConfig, SourceContext, SourceOutput, log_schema,
32 },
33 event::Event,
34 internal_events::{
35 SocketBindError, SocketEventsReceived, SocketMode, SocketReceiveError, StreamClosedError,
36 },
37 net,
38 shutdown::ShutdownSignal,
39 sources::util::net::{SocketListenAddr, TcpNullAcker, TcpSource, try_bind_udp_socket},
40 tcp::TcpKeepaliveConfig,
41 tls::{MaybeTlsSettings, TlsSourceConfig},
42};
43
44#[configurable_component(source("syslog", "Collect logs sent via Syslog."))]
46#[derive(Clone, Debug)]
47pub struct SyslogConfig {
48 #[serde(flatten)]
49 mode: Mode,
50
51 #[serde(default = "crate::serde::default_max_length")]
55 #[configurable(metadata(docs::type_unit = "bytes"))]
56 max_length: usize,
57
58 host_key: Option<OptionalValuePath>,
67
68 #[configurable(metadata(docs::hidden))]
70 #[serde(default)]
71 pub log_namespace: Option<bool>,
72}
73
74#[configurable_component]
76#[derive(Clone, Debug)]
77#[serde(tag = "mode", rename_all = "snake_case")]
78#[configurable(metadata(docs::enum_tag_description = "The type of socket to use."))]
79#[allow(clippy::large_enum_variant)]
80pub enum Mode {
81 Tcp {
83 #[configurable(derived)]
84 address: SocketListenAddr,
85
86 #[configurable(derived)]
87 keepalive: Option<TcpKeepaliveConfig>,
88
89 #[configurable(derived)]
90 permit_origin: Option<IpAllowlistConfig>,
91
92 #[configurable(derived)]
93 tls: Option<TlsSourceConfig>,
94
95 #[configurable(metadata(docs::type_unit = "bytes"))]
99 receive_buffer_bytes: Option<usize>,
100
101 connection_limit: Option<u32>,
103
104 #[configurable(metadata(docs::type_unit = "seconds"))]
110 tls_handshake_timeout_secs: Option<NonZeroU64>,
111 },
112
113 Udp {
115 #[configurable(derived)]
116 address: SocketListenAddr,
117
118 #[configurable(metadata(docs::type_unit = "bytes"))]
122 receive_buffer_bytes: Option<usize>,
123 },
124
125 #[cfg(unix)]
129 Unix {
130 #[configurable(metadata(docs::examples = "/path/to/socket"))]
134 path: PathBuf,
135
136 socket_file_mode: Option<u32>,
141 },
142}
143
144impl SyslogConfig {
145 #[cfg(test)]
146 pub fn from_mode(mode: Mode) -> Self {
147 Self {
148 mode,
149 host_key: None,
150 max_length: crate::serde::default_max_length(),
151 log_namespace: None,
152 }
153 }
154}
155
156impl Default for SyslogConfig {
157 fn default() -> Self {
158 Self {
159 mode: Mode::Tcp {
160 address: SocketListenAddr::SocketAddr("0.0.0.0:514".parse().unwrap()),
161 keepalive: None,
162 permit_origin: None,
163 tls: None,
164 receive_buffer_bytes: None,
165 connection_limit: None,
166 tls_handshake_timeout_secs: None,
167 },
168 host_key: None,
169 max_length: crate::serde::default_max_length(),
170 log_namespace: None,
171 }
172 }
173}
174
175impl GenerateConfig for SyslogConfig {
176 fn generate_config() -> serde_json::Value {
177 serde_json::to_value(SyslogConfig::default()).unwrap()
178 }
179}
180
181#[async_trait::async_trait]
182#[typetag::serde(name = "syslog")]
183impl SourceConfig for SyslogConfig {
184 async fn build(&self, cx: SourceContext) -> crate::Result<super::Source> {
185 let log_namespace = cx.log_namespace(self.log_namespace);
186 let host_key = self
187 .host_key
188 .clone()
189 .and_then(|k| k.path)
190 .or(log_schema().host_key().cloned());
191
192 match self.mode.clone() {
193 Mode::Tcp {
194 address,
195 keepalive,
196 permit_origin,
197 tls,
198 receive_buffer_bytes,
199 connection_limit,
200 tls_handshake_timeout_secs,
201 } => {
202 let source = SyslogTcpSource {
203 max_length: self.max_length,
204 host_key,
205 log_namespace,
206 };
207 let shutdown_secs = Duration::from_secs(30);
208 let tls_config = tls.as_ref().map(|tls| tls.tls_config.clone());
209 let tls_client_metadata_key = tls
210 .as_ref()
211 .and_then(|tls| tls.client_metadata_key.clone())
212 .and_then(|k| k.path);
213 let tls = MaybeTlsSettings::from_config(tls_config.as_ref(), true)?;
214 source.run(
215 address,
216 keepalive,
217 shutdown_secs,
218 tls,
219 None, tls_client_metadata_key,
221 receive_buffer_bytes,
222 None,
223 tls_handshake_timeout_secs,
224 cx,
225 false.into(),
226 connection_limit,
227 permit_origin.map(Into::into),
228 SyslogConfig::NAME,
229 log_namespace,
230 )
231 }
232 Mode::Udp {
233 address,
234 receive_buffer_bytes,
235 } => Ok(udp(
236 address,
237 self.max_length,
238 host_key,
239 receive_buffer_bytes,
240 cx.shutdown,
241 log_namespace,
242 cx.out,
243 )),
244 #[cfg(unix)]
245 Mode::Unix {
246 path,
247 socket_file_mode,
248 } => {
249 let decoder = Decoder::new(
250 Framer::OctetCounting(OctetCountingDecoder::new_with_max_length(
251 self.max_length,
252 )),
253 Deserializer::Syslog(
254 SyslogDeserializerConfig::from_source(SyslogConfig::NAME).build(),
255 ),
256 );
257
258 build_unix_stream_source(
259 path,
260 socket_file_mode,
261 decoder,
262 move |events, host| handle_events(events, &host_key, host, log_namespace),
263 cx.shutdown,
264 cx.out,
265 )
266 }
267 }
268 }
269
270 fn outputs(&self, global_log_namespace: LogNamespace) -> Vec<SourceOutput> {
271 let log_namespace = global_log_namespace.merge(self.log_namespace);
272 let schema_definition = SyslogDeserializerConfig::from_source(SyslogConfig::NAME)
273 .schema_definition(log_namespace)
274 .with_standard_vector_source_metadata();
275
276 vec![SourceOutput::new_maybe_logs(
277 DataType::Log,
278 schema_definition,
279 )]
280 }
281
282 fn resources(&self) -> Vec<Resource> {
283 match self.mode.clone() {
284 Mode::Tcp { address, .. } => vec![address.as_tcp_resource()],
285 Mode::Udp { address, .. } => vec![address.as_udp_resource()],
286 #[cfg(unix)]
287 Mode::Unix { .. } => vec![],
288 }
289 }
290
291 fn can_acknowledge(&self) -> bool {
292 false
293 }
294}
295
296#[derive(Debug, Clone)]
297struct SyslogTcpSource {
298 max_length: usize,
299 host_key: Option<OwnedValuePath>,
300 log_namespace: LogNamespace,
301}
302
303impl TcpSource for SyslogTcpSource {
304 type Error = vector_lib::codecs::decoding::Error;
305 type Item = SmallVec<[Event; 1]>;
306 type Decoder = Decoder;
307 type Acker = TcpNullAcker;
308
309 fn decoder(&self) -> Self::Decoder {
310 Decoder::new(
311 Framer::OctetCounting(OctetCountingDecoder::new_with_max_length(self.max_length)),
312 Deserializer::Syslog(SyslogDeserializerConfig::from_source(SyslogConfig::NAME).build()),
313 )
314 }
315
316 fn handle_events(&self, events: &mut [Event], host: SocketAddr) {
317 handle_events(
318 events,
319 &self.host_key,
320 Some(host.ip().to_string().into()),
321 self.log_namespace,
322 );
323 }
324
325 fn build_acker(&self, _: &[Self::Item]) -> Self::Acker {
326 TcpNullAcker
327 }
328}
329
330pub fn udp(
331 addr: SocketListenAddr,
332 _max_length: usize,
333 host_key: Option<OwnedValuePath>,
334 receive_buffer_bytes: Option<usize>,
335 shutdown: ShutdownSignal,
336 log_namespace: LogNamespace,
337 mut out: SourceSender,
338) -> super::Source {
339 Box::pin(async move {
340 let listenfd = ListenFd::from_env();
341 let socket = try_bind_udp_socket(addr, listenfd).await.map_err(|error| {
342 emit!(SocketBindError {
343 mode: SocketMode::Udp,
344 error: &error,
345 })
346 })?;
347
348 if let Some(receive_buffer_bytes) = receive_buffer_bytes
349 && let Err(error) = net::set_receive_buffer_size(&socket, receive_buffer_bytes)
350 {
351 warn!(message = "Failed configuring receive buffer size on UDP socket.", %error);
352 }
353
354 info!(
355 message = "Listening.",
356 addr = %addr,
357 r#type = "udp"
358 );
359
360 let bytes_received = register!(BytesReceived::from(Protocol::UDP));
361
362 let mut stream = UdpFramed::new(
363 socket,
364 Decoder::new(
365 Framer::Bytes(BytesDecoder::new()),
366 Deserializer::Syslog(
367 SyslogDeserializerConfig::from_source(SyslogConfig::NAME).build(),
368 ),
369 ),
370 )
371 .take_until(shutdown)
372 .filter_map(|frame| {
373 let host_key = host_key.clone();
374 let bytes_received = bytes_received.clone();
375 async move {
376 match frame {
377 Ok(((mut events, byte_size), received_from)) => {
378 let count = events.len();
379 bytes_received.emit(ByteSize(byte_size));
380 emit!(SocketEventsReceived {
381 mode: SocketMode::Udp,
382 byte_size: events.estimated_json_encoded_size_of(),
383 count,
384 });
385 let received_from = received_from.ip().to_string().into();
386 handle_events(&mut events, &host_key, Some(received_from), log_namespace);
387 Some(events.remove(0))
388 }
389 Err(error) => {
390 emit!(SocketReceiveError {
391 mode: SocketMode::Udp,
392 error: &error,
393 });
394 None
395 }
396 }
397 }
398 })
399 .boxed();
400
401 match out.send_event_stream(&mut stream).await {
402 Ok(()) => {
403 debug!("Finished sending.");
404 Ok(())
405 }
406 Err(_) => {
407 let (count, _) = stream.size_hint();
408 emit!(StreamClosedError { count });
409 Err(())
410 }
411 }
412 })
413}
414
415fn handle_events(
416 events: &mut [Event],
417 host_key: &Option<OwnedValuePath>,
418 default_host: Option<Bytes>,
419 log_namespace: LogNamespace,
420) {
421 for event in events {
422 enrich_syslog_event(event, host_key, default_host.clone(), log_namespace);
423 }
424}
425
426fn enrich_syslog_event(
427 event: &mut Event,
428 host_key: &Option<OwnedValuePath>,
429 default_host: Option<Bytes>,
430 log_namespace: LogNamespace,
431) {
432 let log = event.as_mut_log();
433
434 if let Some(default_host) = &default_host {
435 log_namespace.insert_source_metadata(
436 SyslogConfig::NAME,
437 log,
438 Some(LegacyKey::Overwrite(path!("source_ip"))),
439 path!("source_ip"),
440 default_host.clone(),
441 );
442 }
443
444 let parsed_hostname = log
445 .get(event_path!("hostname"))
446 .map(|hostname| hostname.coerce_to_bytes());
447
448 if let Some(parsed_host) = parsed_hostname.or(default_host) {
449 let legacy_host_key = host_key.as_ref().map(LegacyKey::Overwrite);
450
451 log_namespace.insert_source_metadata(
452 SyslogConfig::NAME,
453 log,
454 legacy_host_key,
455 path!("host"),
456 parsed_host,
457 );
458 }
459
460 log_namespace.insert_standard_vector_source_metadata(log, SyslogConfig::NAME, Utc::now());
461
462 if log_namespace == LogNamespace::Legacy {
463 let timestamp = log
464 .get(event_path!("timestamp"))
465 .and_then(|timestamp| timestamp.as_timestamp().cloned())
466 .unwrap_or_else(Utc::now);
467 log.maybe_insert(log_schema().timestamp_key_target_path(), timestamp);
468 }
469
470 trace!(
471 message = "Processing one event.",
472 event = ?event
473 );
474}
475
476#[cfg(test)]
477mod test {
478 use std::{collections::HashMap, fmt, str::FromStr};
479
480 use chrono::prelude::*;
481 use indoc::indoc;
482 use rand::{RngExt, rng};
483 use serde::Deserialize;
484 use tokio::time::{Duration, Instant, sleep};
485 use tokio_util::codec::BytesCodec;
486 use vector_lib::{
487 assert_event_data_eq,
488 codecs::decoding::format::Deserializer,
489 config::ComponentKey,
490 lookup::{OwnedTargetPath, PathPrefix, event_path, owned_value_path},
491 schema::Definition,
492 };
493 use vrl::value::{Kind, ObjectMap, Value, kind::Collection};
494
495 use super::*;
496 use crate::{
497 config::log_schema,
498 event::{Event, LogEvent},
499 test_util::{
500 CountReceiver,
501 addr::next_addr,
502 components::{SOCKET_PUSH_SOURCE_TAGS, assert_source_compliance},
503 random_maps, random_string, send_encodable, send_lines, wait_for_tcp,
504 },
505 };
506
507 fn event_from_bytes(
508 host_key: &str,
509 default_host: Option<Bytes>,
510 bytes: Bytes,
511 log_namespace: LogNamespace,
512 ) -> Option<Event> {
513 let parser = SyslogDeserializerConfig::from_source(SyslogConfig::NAME).build();
514 let mut events = parser.parse(bytes, LogNamespace::Legacy).ok()?;
515 handle_events(
516 &mut events,
517 &Some(owned_value_path!(host_key)),
518 default_host,
519 log_namespace,
520 );
521 Some(events.remove(0))
522 }
523
524 #[test]
525 fn generate_config() {
526 crate::test_util::test_generate_config::<SyslogConfig>();
527 }
528
529 #[test]
530 fn output_schema_definition_vector_namespace() {
531 let config = SyslogConfig {
532 log_namespace: Some(true),
533 ..Default::default()
534 };
535
536 let definitions = config
537 .outputs(LogNamespace::Vector)
538 .remove(0)
539 .schema_definition(true);
540
541 let expected_definition =
542 Definition::new_with_default_metadata(Kind::bytes(), [LogNamespace::Vector])
543 .with_meaning(OwnedTargetPath::event_root(), "message")
544 .with_metadata_field(
545 &owned_value_path!("vector", "source_type"),
546 Kind::bytes(),
547 None,
548 )
549 .with_metadata_field(
550 &owned_value_path!("vector", "ingest_timestamp"),
551 Kind::timestamp(),
552 None,
553 )
554 .with_metadata_field(
555 &owned_value_path!("syslog", "timestamp"),
556 Kind::timestamp(),
557 Some("timestamp"),
558 )
559 .with_metadata_field(
560 &owned_value_path!("syslog", "hostname"),
561 Kind::bytes().or_undefined(),
562 Some("host"),
563 )
564 .with_metadata_field(
565 &owned_value_path!("syslog", "source_ip"),
566 Kind::bytes().or_undefined(),
567 None,
568 )
569 .with_metadata_field(
570 &owned_value_path!("syslog", "severity"),
571 Kind::bytes().or_undefined(),
572 Some("severity"),
573 )
574 .with_metadata_field(
575 &owned_value_path!("syslog", "facility"),
576 Kind::bytes().or_undefined(),
577 None,
578 )
579 .with_metadata_field(
580 &owned_value_path!("syslog", "version"),
581 Kind::integer().or_undefined(),
582 None,
583 )
584 .with_metadata_field(
585 &owned_value_path!("syslog", "appname"),
586 Kind::bytes().or_undefined(),
587 Some("service"),
588 )
589 .with_metadata_field(
590 &owned_value_path!("syslog", "msgid"),
591 Kind::bytes().or_undefined(),
592 None,
593 )
594 .with_metadata_field(
595 &owned_value_path!("syslog", "procid"),
596 Kind::integer().or_bytes().or_undefined(),
597 None,
598 )
599 .with_metadata_field(
600 &owned_value_path!("syslog", "structured_data"),
601 Kind::object(Collection::from_unknown(Kind::object(
602 Collection::from_unknown(Kind::bytes()),
603 ))),
604 None,
605 )
606 .with_metadata_field(
607 &owned_value_path!("syslog", "tls_client_metadata"),
608 Kind::object(Collection::empty().with_unknown(Kind::bytes())).or_undefined(),
609 None,
610 );
611
612 assert_eq!(definitions, Some(expected_definition));
613 }
614
615 #[test]
616 fn output_schema_definition_legacy_namespace() {
617 let config = SyslogConfig::default();
618
619 let definitions = config
620 .outputs(LogNamespace::Legacy)
621 .remove(0)
622 .schema_definition(true);
623
624 let expected_definition = Definition::new_with_default_metadata(
625 Kind::object(Collection::empty()),
626 [LogNamespace::Legacy],
627 )
628 .with_event_field(
629 &owned_value_path!("message"),
630 Kind::bytes(),
631 Some("message"),
632 )
633 .with_event_field(
634 &owned_value_path!("timestamp"),
635 Kind::timestamp(),
636 Some("timestamp"),
637 )
638 .with_event_field(
639 &owned_value_path!("hostname"),
640 Kind::bytes().or_undefined(),
641 Some("host"),
642 )
643 .with_event_field(
644 &owned_value_path!("source_ip"),
645 Kind::bytes().or_undefined(),
646 None,
647 )
648 .with_event_field(
649 &owned_value_path!("severity"),
650 Kind::bytes().or_undefined(),
651 Some("severity"),
652 )
653 .with_event_field(
654 &owned_value_path!("facility"),
655 Kind::bytes().or_undefined(),
656 None,
657 )
658 .with_event_field(
659 &owned_value_path!("version"),
660 Kind::integer().or_undefined(),
661 None,
662 )
663 .with_event_field(
664 &owned_value_path!("appname"),
665 Kind::bytes().or_undefined(),
666 Some("service"),
667 )
668 .with_event_field(
669 &owned_value_path!("msgid"),
670 Kind::bytes().or_undefined(),
671 None,
672 )
673 .with_event_field(
674 &owned_value_path!("procid"),
675 Kind::integer().or_bytes().or_undefined(),
676 None,
677 )
678 .unknown_fields(Kind::object(Collection::from_unknown(Kind::bytes())))
679 .with_standard_vector_source_metadata();
680
681 assert_eq!(definitions, Some(expected_definition));
682 }
683
684 #[test]
685 fn config_tcp() {
686 let config: SyslogConfig = serde_yaml::from_str(indoc! {
687 r#"
688 mode: tcp
689 address: "127.0.0.1:1235"
690 "#,
691 })
692 .unwrap();
693 assert!(matches!(config.mode, Mode::Tcp { .. }));
694 }
695
696 #[test]
697 fn config_tcp_with_receive_buffer_size() {
698 let config: SyslogConfig = serde_yaml::from_str(indoc! {
699 r#"
700 mode: tcp
701 address: "127.0.0.1:1235"
702 receive_buffer_bytes: 256
703 "#,
704 })
705 .unwrap();
706
707 let receive_buffer_bytes = match config.mode {
708 Mode::Tcp {
709 receive_buffer_bytes,
710 ..
711 } => receive_buffer_bytes,
712 _ => panic!("expected Mode::Tcp"),
713 };
714
715 assert_eq!(receive_buffer_bytes, Some(256));
716 }
717
718 #[test]
719 fn config_tcp_keepalive_empty() {
720 let config: SyslogConfig = serde_yaml::from_str(indoc! {
721 r#"
722 mode: tcp
723 address: "127.0.0.1:1235"
724 "#,
725 })
726 .unwrap();
727
728 let keepalive = match config.mode {
729 Mode::Tcp { keepalive, .. } => keepalive,
730 _ => panic!("expected Mode::Tcp"),
731 };
732
733 assert_eq!(keepalive, None);
734 }
735
736 #[test]
737 fn config_tcp_keepalive_full() {
738 let config: SyslogConfig = serde_yaml::from_str(indoc! {
739 r#"
740 mode: tcp
741 address: "127.0.0.1:1235"
742 keepalive:
743 time_secs: 7200
744 "#,
745 })
746 .unwrap();
747
748 let keepalive = match config.mode {
749 Mode::Tcp { keepalive, .. } => keepalive,
750 _ => panic!("expected Mode::Tcp"),
751 };
752
753 let keepalive = keepalive.expect("keepalive config not set");
754
755 assert_eq!(keepalive.time_secs, Some(7200));
756 }
757
758 #[test]
759 fn config_udp() {
760 let config: SyslogConfig = serde_yaml::from_str(indoc! {
761 r#"
762 mode: udp
763 address: "127.0.0.1:1235"
764 max_length: 32187
765 "#,
766 })
767 .unwrap();
768 assert!(matches!(config.mode, Mode::Udp { .. }));
769 }
770
771 #[test]
772 fn config_udp_with_receive_buffer_size() {
773 let config: SyslogConfig = serde_yaml::from_str(indoc! {
774 r#"
775 mode: udp
776 address: "127.0.0.1:1235"
777 max_length: 32187
778 receive_buffer_bytes: 256
779 "#,
780 })
781 .unwrap();
782
783 let receive_buffer_bytes = match config.mode {
784 Mode::Udp {
785 receive_buffer_bytes,
786 ..
787 } => receive_buffer_bytes,
788 _ => panic!("expected Mode::Udp"),
789 };
790
791 assert_eq!(receive_buffer_bytes, Some(256));
792 }
793
794 #[cfg(unix)]
795 #[test]
796 fn config_unix() {
797 let config: SyslogConfig = serde_yaml::from_str(indoc! {
798 r#"
799 mode: unix
800 path: "127.0.0.1:1235"
801 "#,
802 })
803 .unwrap();
804 assert!(matches!(config.mode, Mode::Unix { .. }));
805 }
806
807 #[cfg(unix)]
808 #[test]
809 fn config_unix_permissions() {
810 let config: SyslogConfig = serde_yaml::from_str(indoc! {
811 r#"
812 mode: unix
813 path: "127.0.0.1:1235"
814 socket_file_mode: 511
815 "#,
816 })
817 .unwrap();
818 let socket_file_mode = match config.mode {
819 Mode::Unix {
820 path: _,
821 socket_file_mode,
822 } => socket_file_mode,
823 _ => panic!("expected Mode::Unix"),
824 };
825
826 assert_eq!(socket_file_mode, Some(0o777));
827 }
828
829 #[test]
830 fn syslog_ng_network_syslog_protocol() {
831 let msg = "i am foobar";
833 let raw = format!(
834 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {}{} {}"#,
835 r#"[meta sequenceId="1" sysUpTime="37" language="EN"]"#,
836 r#"[origin ip="192.168.0.1" software="test"]"#,
837 msg
838 );
839
840 let mut expected = Event::Log(LogEvent::from(msg));
841
842 {
843 let expected = expected.as_mut_log();
844 expected.insert(
845 (PathPrefix::Event, log_schema().timestamp_key().unwrap()),
846 Utc.with_ymd_and_hms(2019, 2, 13, 19, 48, 34)
847 .single()
848 .expect("invalid timestamp"),
849 );
850 expected.insert(
851 log_schema().source_type_key_target_path().unwrap(),
852 "syslog",
853 );
854 expected.insert(event_path!("host"), "74794bfb6795");
855 expected.insert(event_path!("hostname"), "74794bfb6795");
856
857 expected.insert(event_path!("meta", "sequenceId"), "1");
858 expected.insert(event_path!("meta", "sysUpTime"), "37");
859 expected.insert(event_path!("meta", "language"), "EN");
860 expected.insert(event_path!("origin", "software"), "test");
861 expected.insert(event_path!("origin", "ip"), "192.168.0.1");
862
863 expected.insert(event_path!("severity"), "notice");
864 expected.insert(event_path!("facility"), "user");
865 expected.insert(event_path!("version"), 1);
866 expected.insert(event_path!("appname"), "root");
867 expected.insert(event_path!("procid"), 8449);
868 expected.insert(event_path!("source_ip"), "192.168.0.254");
869 }
870
871 assert_event_data_eq!(
872 event_from_bytes(
873 "host",
874 Some(Bytes::from("192.168.0.254")),
875 raw.into(),
876 LogNamespace::Legacy
877 )
878 .unwrap(),
879 expected
880 );
881 }
882
883 #[test]
884 fn handles_incorrect_sd_element() {
885 let msg = "qwerty";
886 let raw = format!(
887 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} {}"#,
888 r"[incorrect x]", msg
889 );
890
891 let mut expected = Event::Log(LogEvent::from(msg));
892 {
893 let expected = expected.as_mut_log();
894 expected.insert(
895 (PathPrefix::Event, log_schema().timestamp_key().unwrap()),
896 Utc.with_ymd_and_hms(2019, 2, 13, 19, 48, 34)
897 .single()
898 .expect("invalid timestamp"),
899 );
900 expected.insert(
901 (PathPrefix::Event, log_schema().host_key().unwrap()),
902 "74794bfb6795",
903 );
904 expected.insert(event_path!("hostname"), "74794bfb6795");
905 expected.insert(
906 log_schema().source_type_key_target_path().unwrap(),
907 "syslog",
908 );
909 expected.insert(event_path!("severity"), "notice");
910 expected.insert(event_path!("facility"), "user");
911 expected.insert(event_path!("version"), 1);
912 expected.insert(event_path!("appname"), "root");
913 expected.insert(event_path!("procid"), 8449);
914 expected.insert(event_path!("source_ip"), "192.168.0.254");
915 }
916
917 let event = event_from_bytes(
918 "host",
919 Some(Bytes::from("192.168.0.254")),
920 raw.into(),
921 LogNamespace::Legacy,
922 )
923 .unwrap();
924 assert_event_data_eq!(event, expected);
925
926 let raw = format!(
927 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} {}"#,
928 r"[incorrect x=]", msg
929 );
930
931 let event = event_from_bytes(
932 "host",
933 Some(Bytes::from("192.168.0.254")),
934 raw.into(),
935 LogNamespace::Legacy,
936 )
937 .unwrap();
938 assert_event_data_eq!(event, expected);
939 }
940
941 #[test]
942 fn handles_empty_sd_element() {
943 fn there_is_map_called_empty(event: Event) -> bool {
944 event
945 .as_log()
946 .get(event_path!("empty"))
947 .expect("empty exists")
948 .is_object()
949 }
950
951 let msg = format!(
952 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
953 r"[empty]"
954 );
955
956 let event = event_from_bytes("host", None, msg.into(), LogNamespace::Legacy).unwrap();
957 assert!(there_is_map_called_empty(event));
958
959 let msg = format!(
960 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
961 r#"[non_empty x="1"][empty]"#
962 );
963
964 let event = event_from_bytes("host", None, msg.into(), LogNamespace::Legacy).unwrap();
965 assert!(there_is_map_called_empty(event));
966
967 let msg = format!(
968 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
969 r#"[empty][non_empty x="1"]"#
970 );
971
972 let event = event_from_bytes("host", None, msg.into(), LogNamespace::Legacy).unwrap();
973 assert!(there_is_map_called_empty(event));
974
975 let msg = format!(
976 r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - {} qwerty"#,
977 r#"[empty not_really="testing the test"]"#
978 );
979
980 let event = event_from_bytes("host", None, msg.into(), LogNamespace::Legacy).unwrap();
981 assert!(there_is_map_called_empty(event));
982 }
983
984 #[test]
985 fn handles_weird_whitespace() {
986 let raw = r#"
988 <13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - [meta sequenceId="1"] i am foobar
989 "#;
990 let cleaned = r#"<13>1 2019-02-13T19:48:34+00:00 74794bfb6795 root 8449 - [meta sequenceId="1"] i am foobar"#;
991
992 assert_event_data_eq!(
993 event_from_bytes("host", None, raw.to_owned().into(), LogNamespace::Legacy).unwrap(),
994 event_from_bytes(
995 "host",
996 None,
997 cleaned.to_owned().into(),
998 LogNamespace::Legacy
999 )
1000 .unwrap()
1001 );
1002 }
1003
1004 #[test]
1005 fn handles_dots_in_sdata() {
1006 let raw =
1007 r#"<190>Feb 13 21:31:56 74794bfb6795 liblogging-stdlog: [origin foo.bar="baz"] hello"#;
1008 let event =
1009 event_from_bytes("host", None, raw.to_owned().into(), LogNamespace::Legacy).unwrap();
1010 assert_eq!(
1011 event.as_log().get(event_path!("origin", "foo.bar")),
1012 Some(&Value::from("baz"))
1013 );
1014 }
1015
1016 #[test]
1017 fn syslog_ng_default_network() {
1018 let msg = "i am foobar";
1019 let raw = format!(r#"<13>Feb 13 20:07:26 74794bfb6795 root[8539]: {msg}"#);
1020 let event = event_from_bytes(
1021 "host",
1022 Some(Bytes::from("192.168.0.254")),
1023 raw.into(),
1024 LogNamespace::Legacy,
1025 )
1026 .unwrap();
1027
1028 let mut expected = Event::Log(LogEvent::from(msg));
1029 {
1030 let value = event.as_log().get(event_path!("timestamp")).unwrap();
1031 let year = value.as_timestamp().unwrap().naive_local().year();
1032
1033 let expected = expected.as_mut_log();
1034 let expected_date: DateTime<Utc> = Local
1035 .with_ymd_and_hms(year, 2, 13, 20, 7, 26)
1036 .single()
1037 .expect("invalid timestamp")
1038 .into();
1039
1040 expected.insert(
1041 (PathPrefix::Event, log_schema().timestamp_key().unwrap()),
1042 expected_date,
1043 );
1044 expected.insert(
1045 (PathPrefix::Event, log_schema().host_key().unwrap()),
1046 "74794bfb6795",
1047 );
1048 expected.insert(
1049 log_schema().source_type_key_target_path().unwrap(),
1050 "syslog",
1051 );
1052 expected.insert(event_path!("hostname"), "74794bfb6795");
1053 expected.insert(event_path!("severity"), "notice");
1054 expected.insert(event_path!("facility"), "user");
1055 expected.insert(event_path!("appname"), "root");
1056 expected.insert(event_path!("procid"), 8539);
1057 expected.insert(event_path!("source_ip"), "192.168.0.254");
1058 }
1059
1060 assert_event_data_eq!(event, expected);
1061 }
1062
1063 #[test]
1064 fn rsyslog_omfwd_tcp_default() {
1065 let msg = "start";
1066 let raw = format!(
1067 r#"<190>Feb 13 21:31:56 74794bfb6795 liblogging-stdlog: [origin software="rsyslogd" swVersion="8.24.0" x-pid="8979" x-info="http://www.rsyslog.com"] {msg}"#
1068 );
1069 let event = event_from_bytes(
1070 "host",
1071 Some(Bytes::from("192.168.0.254")),
1072 raw.into(),
1073 LogNamespace::Legacy,
1074 )
1075 .unwrap();
1076
1077 let mut expected = Event::Log(LogEvent::from(msg));
1078 {
1079 let value = event.as_log().get(event_path!("timestamp")).unwrap();
1080 let year = value.as_timestamp().unwrap().naive_local().year();
1081
1082 let expected = expected.as_mut_log();
1083 let expected_date: DateTime<Utc> = Local
1084 .with_ymd_and_hms(year, 2, 13, 21, 31, 56)
1085 .single()
1086 .expect("invalid timestamp")
1087 .into();
1088 expected.insert(
1089 (PathPrefix::Event, log_schema().timestamp_key().unwrap()),
1090 expected_date,
1091 );
1092 expected.insert(
1093 log_schema().source_type_key_target_path().unwrap(),
1094 "syslog",
1095 );
1096 expected.insert(event_path!("host"), "74794bfb6795");
1097 expected.insert(event_path!("hostname"), "74794bfb6795");
1098 expected.insert(event_path!("severity"), "info");
1099 expected.insert(event_path!("facility"), "local7");
1100 expected.insert(event_path!("appname"), "liblogging-stdlog");
1101 expected.insert(event_path!("origin", "software"), "rsyslogd");
1102 expected.insert(event_path!("origin", "swVersion"), "8.24.0");
1103 expected.insert(event_path!("source_ip"), "192.168.0.254");
1104 expected.insert(event_path!("origin", "x-pid"), "8979");
1105 expected.insert(event_path!("origin", "x-info"), "http://www.rsyslog.com");
1106 }
1107
1108 assert_event_data_eq!(event, expected);
1109 }
1110
1111 #[test]
1112 fn rsyslog_omfwd_tcp_forward_format() {
1113 let msg = "start";
1114 let raw = format!(
1115 r#"<190>2019-02-13T21:53:30.605850+00:00 74794bfb6795 liblogging-stdlog: [origin software="rsyslogd" swVersion="8.24.0" x-pid="9043" x-info="http://www.rsyslog.com"] {msg}"#
1116 );
1117
1118 let mut expected = Event::Log(LogEvent::from(msg));
1119 {
1120 let expected = expected.as_mut_log();
1121 expected.insert(
1122 (PathPrefix::Event, log_schema().timestamp_key().unwrap()),
1123 Utc.with_ymd_and_hms(2019, 2, 13, 21, 53, 30)
1124 .single()
1125 .and_then(|t| t.with_nanosecond(605_850 * 1000))
1126 .expect("invalid timestamp"),
1127 );
1128 expected.insert(
1129 log_schema().source_type_key_target_path().unwrap(),
1130 "syslog",
1131 );
1132 expected.insert(event_path!("host"), "74794bfb6795");
1133 expected.insert(event_path!("hostname"), "74794bfb6795");
1134 expected.insert(event_path!("severity"), "info");
1135 expected.insert(event_path!("facility"), "local7");
1136 expected.insert(event_path!("appname"), "liblogging-stdlog");
1137 expected.insert(event_path!("origin", "software"), "rsyslogd");
1138 expected.insert(event_path!("origin", "swVersion"), "8.24.0");
1139 expected.insert(event_path!("origin", "x-pid"), "9043");
1140 expected.insert(event_path!("origin", "x-info"), "http://www.rsyslog.com");
1141 }
1142
1143 assert_event_data_eq!(
1144 event_from_bytes("host", None, raw.into(), LogNamespace::Legacy).unwrap(),
1145 expected
1146 );
1147 }
1148
1149 #[tokio::test]
1150 async fn test_tcp_syslog() {
1151 assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async {
1152 let num_messages: usize = 10000;
1153 let (_guard, in_addr) = next_addr();
1154
1155 let config = SyslogConfig::from_mode(Mode::Tcp {
1157 address: in_addr.into(),
1158 permit_origin: None,
1159 keepalive: None,
1160 tls: None,
1161 receive_buffer_bytes: None,
1162 connection_limit: None,
1163 tls_handshake_timeout_secs: None,
1164 });
1165
1166 let key = ComponentKey::from("in");
1167 let (tx, rx) = SourceSender::new_test();
1168 let (context, shutdown) = SourceContext::new_shutdown(&key, tx);
1169 let shutdown_complete = shutdown.shutdown_tripwire();
1170
1171 let source = config
1172 .build(context)
1173 .await
1174 .expect("source should not fail to build");
1175 tokio::spawn(source);
1176
1177 wait_for_tcp(in_addr).await;
1179
1180 let output_events = CountReceiver::receive_events(rx);
1181
1182 let input_messages: Vec<SyslogMessageRfc5424> = (0..num_messages)
1184 .map(|i| SyslogMessageRfc5424::random(i, 30, 4, 3, 3))
1185 .collect();
1186
1187 let input_lines: Vec<String> =
1188 input_messages.iter().map(|msg| msg.to_string()).collect();
1189
1190 send_lines(in_addr, input_lines).await.unwrap();
1191
1192 sleep(Duration::from_secs(2)).await;
1194
1195 shutdown
1197 .shutdown_all(Some(Instant::now() + Duration::from_millis(100)))
1198 .await;
1199 shutdown_complete.await;
1200
1201 let output_events = output_events.await;
1202 assert_eq!(output_events.len(), num_messages);
1203
1204 let output_messages: Vec<SyslogMessageRfc5424> = output_events
1205 .into_iter()
1206 .map(|mut e| {
1207 e.as_mut_log().remove(event_path!("hostname")); e.as_mut_log().remove(event_path!("source_ip")); e.into()
1210 })
1211 .collect();
1212 assert_eq!(output_messages, input_messages);
1213 })
1214 .await;
1215 }
1216
1217 #[tokio::test]
1218 async fn test_udp_syslog() {
1219 assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async {
1220 let num_messages: usize = 1000;
1221 let (_guard, in_addr) = next_addr();
1222
1223 let config = SyslogConfig::from_mode(Mode::Udp {
1225 address: in_addr.into(),
1226 receive_buffer_bytes: Some(4 * 1024 * 1024),
1227 });
1228
1229 let key = ComponentKey::from("in");
1230 let (tx, rx) = SourceSender::new_test();
1231 let (context, shutdown) = SourceContext::new_shutdown(&key, tx);
1232 let shutdown_complete = shutdown.shutdown_tripwire();
1233
1234 let source = config
1235 .build(context)
1236 .await
1237 .expect("source should not fail to build");
1238 tokio::spawn(source);
1239
1240 sleep(Duration::from_millis(150)).await;
1242
1243 let output_events = CountReceiver::receive_events(rx);
1244
1245 let input_messages: Vec<SyslogMessageRfc5424> = (0..num_messages)
1247 .map(|i| SyslogMessageRfc5424::random(i, 30, 4, 3, 3))
1248 .collect();
1249
1250 let input_lines: Vec<String> =
1251 input_messages.iter().map(|msg| msg.to_string()).collect();
1252
1253 let socket = tokio::net::UdpSocket::bind("127.0.0.1:0").await.unwrap();
1254 for line in input_lines {
1255 socket.send_to(line.as_bytes(), in_addr).await.unwrap();
1256 }
1257
1258 sleep(Duration::from_secs(2)).await;
1260
1261 shutdown
1263 .shutdown_all(Some(Instant::now() + Duration::from_millis(100)))
1264 .await;
1265 shutdown_complete.await;
1266
1267 let output_events = output_events.await;
1268 assert_eq!(output_events.len(), num_messages);
1269
1270 let output_messages: Vec<SyslogMessageRfc5424> = output_events
1271 .into_iter()
1272 .map(|mut e| {
1273 e.as_mut_log().remove(event_path!("hostname")); e.as_mut_log().remove(event_path!("source_ip")); e.into()
1276 })
1277 .collect();
1278 assert_eq!(output_messages, input_messages);
1279 })
1280 .await;
1281 }
1282
1283 #[cfg(unix)]
1284 #[tokio::test]
1285 async fn test_unix_stream_syslog() {
1286 use std::os::unix::net::UnixStream as StdUnixStream;
1287
1288 use futures_util::{SinkExt, stream};
1289 use tokio::{io::AsyncWriteExt, net::UnixStream};
1290 use tokio_util::codec::{FramedWrite, LinesCodec};
1291
1292 use crate::test_util::components::SOCKET_PUSH_SOURCE_TAGS;
1293
1294 assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async {
1295 let num_messages: usize = 1;
1296 let in_path = tempfile::tempdir().unwrap().keep().join("stream_test");
1297
1298 let config = SyslogConfig::from_mode(Mode::Unix {
1300 path: in_path.clone(),
1301 socket_file_mode: None,
1302 });
1303
1304 let key = ComponentKey::from("in");
1305 let (tx, rx) = SourceSender::new_test();
1306 let (context, shutdown) = SourceContext::new_shutdown(&key, tx);
1307 let shutdown_complete = shutdown.shutdown_tripwire();
1308
1309 let source = config
1310 .build(context)
1311 .await
1312 .expect("source should not fail to build");
1313 tokio::spawn(source);
1314
1315 while StdUnixStream::connect(&in_path).is_err() {
1317 tokio::task::yield_now().await;
1318 }
1319
1320 let output_events = CountReceiver::receive_events(rx);
1321
1322 let input_messages: Vec<SyslogMessageRfc5424> = (0..num_messages)
1324 .map(|i| SyslogMessageRfc5424::random(i, 30, 4, 3, 3))
1325 .collect();
1326
1327 let stream = UnixStream::connect(&in_path).await.unwrap();
1328 let mut sink = FramedWrite::new(stream, LinesCodec::new());
1329
1330 let lines: Vec<String> = input_messages.iter().map(|msg| msg.to_string()).collect();
1331 let mut lines = stream::iter(lines).map(Ok);
1332 sink.send_all(&mut lines).await.unwrap();
1333
1334 let stream = sink.get_mut();
1335 stream.shutdown().await.unwrap();
1336
1337 sleep(Duration::from_secs(1)).await;
1339
1340 shutdown
1341 .shutdown_all(Some(Instant::now() + Duration::from_millis(100)))
1342 .await;
1343 shutdown_complete.await;
1344
1345 let output_events = output_events.await;
1346 assert_eq!(output_events.len(), num_messages);
1347
1348 let output_messages: Vec<SyslogMessageRfc5424> = output_events
1349 .into_iter()
1350 .map(|mut e| {
1351 e.as_mut_log().remove(event_path!("hostname")); e.as_mut_log().remove(event_path!("source_ip")); e.into()
1354 })
1355 .collect();
1356 assert_eq!(output_messages, input_messages);
1357 })
1358 .await;
1359 }
1360
1361 #[tokio::test]
1362 async fn test_octet_counting_syslog() {
1363 assert_source_compliance(&SOCKET_PUSH_SOURCE_TAGS, async {
1364 let num_messages: usize = 10000;
1365 let (_guard, in_addr) = next_addr();
1366
1367 let config = SyslogConfig::from_mode(Mode::Tcp {
1369 address: in_addr.into(),
1370 permit_origin: None,
1371 keepalive: None,
1372 tls: None,
1373 receive_buffer_bytes: None,
1374 connection_limit: None,
1375 tls_handshake_timeout_secs: None,
1376 });
1377
1378 let key = ComponentKey::from("in");
1379 let (tx, rx) = SourceSender::new_test();
1380 let (context, shutdown) = SourceContext::new_shutdown(&key, tx);
1381 let shutdown_complete = shutdown.shutdown_tripwire();
1382
1383 let source = config
1384 .build(context)
1385 .await
1386 .expect("source should not fail to build");
1387 tokio::spawn(source);
1388
1389 wait_for_tcp(in_addr).await;
1391
1392 let output_events = CountReceiver::receive_events(rx);
1393
1394 let input_messages: Vec<SyslogMessageRfc5424> = (0..num_messages)
1396 .map(|i| {
1397 let mut msg = SyslogMessageRfc5424::random(i, 30, 4, 3, 3);
1398 msg.message.push('\n');
1399 msg.message.push_str(&random_string(30));
1400 msg
1401 })
1402 .collect();
1403
1404 let codec = BytesCodec::new();
1405 let input_lines: Vec<Bytes> = input_messages
1406 .iter()
1407 .map(|msg| {
1408 let s = msg.to_string();
1409 format!("{} {}", s.len(), s).into()
1410 })
1411 .collect();
1412
1413 send_encodable(in_addr, codec, input_lines).await.unwrap();
1414
1415 sleep(Duration::from_secs(2)).await;
1417
1418 shutdown
1420 .shutdown_all(Some(Instant::now() + Duration::from_millis(100)))
1421 .await;
1422 shutdown_complete.await;
1423
1424 let output_events = output_events.await;
1425 assert_eq!(output_events.len(), num_messages);
1426
1427 let output_messages: Vec<SyslogMessageRfc5424> = output_events
1428 .into_iter()
1429 .map(|mut e| {
1430 e.as_mut_log().remove(event_path!("hostname")); e.as_mut_log().remove(event_path!("source_ip")); e.into()
1433 })
1434 .collect();
1435 assert_eq!(output_messages, input_messages);
1436 })
1437 .await;
1438 }
1439
1440 #[derive(Deserialize, PartialEq, Clone, Debug)]
1441 struct SyslogMessageRfc5424 {
1442 msgid: String,
1443 severity: Severity,
1444 facility: Facility,
1445 version: u8,
1446 timestamp: String,
1447 host: String,
1448 source_type: String,
1449 appname: String,
1450 procid: usize,
1451 message: String,
1452 #[serde(flatten)]
1453 structured_data: StructuredData,
1454 }
1455
1456 impl SyslogMessageRfc5424 {
1457 fn random(
1458 id: usize,
1459 msg_len: usize,
1460 field_len: usize,
1461 max_map_size: usize,
1462 max_children: usize,
1463 ) -> Self {
1464 let msg = random_string(msg_len);
1465 let structured_data = random_structured_data(max_map_size, max_children, field_len);
1466
1467 let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
1468 Self {
1471 msgid: format!("test{id}"),
1472 severity: Severity::LOG_INFO,
1473 facility: Facility::LOG_USER,
1474 version: 1,
1475 timestamp,
1476 host: "hogwarts".to_owned(),
1477 source_type: "syslog".to_owned(),
1478 appname: "harry".to_owned(),
1479 procid: rng().random_range(0..32768),
1480 structured_data,
1481 message: msg,
1482 }
1483 }
1484 }
1485
1486 impl fmt::Display for SyslogMessageRfc5424 {
1487 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1488 write!(
1489 f,
1490 "<{}>{} {} {} {} {} {} {} {}",
1491 encode_priority(self.severity, self.facility),
1492 self.version,
1493 self.timestamp,
1494 self.host,
1495 self.appname,
1496 self.procid,
1497 self.msgid,
1498 format_structured_data_rfc5424(&self.structured_data),
1499 self.message
1500 )
1501 }
1502 }
1503
1504 impl From<Event> for SyslogMessageRfc5424 {
1505 fn from(e: Event) -> Self {
1506 let (value, _) = e.into_log().into_parts();
1507 let mut fields = value.into_object().unwrap();
1508
1509 Self {
1510 msgid: fields.remove("msgid").map(value_to_string).unwrap(),
1511 severity: fields
1512 .remove("severity")
1513 .map(value_to_string)
1514 .and_then(|s| Severity::from_str(s.as_str()))
1515 .unwrap(),
1516 facility: fields
1517 .remove("facility")
1518 .map(value_to_string)
1519 .and_then(|s| Facility::from_str(s.as_str()))
1520 .unwrap(),
1521 version: fields
1522 .remove("version")
1523 .map(value_to_string)
1524 .map(|s| u8::from_str(s.as_str()).unwrap())
1525 .unwrap(),
1526 timestamp: fields.remove("timestamp").map(value_to_string).unwrap(),
1527 host: fields.remove("host").map(value_to_string).unwrap(),
1528 source_type: fields.remove("source_type").map(value_to_string).unwrap(),
1529 appname: fields.remove("appname").map(value_to_string).unwrap(),
1530 procid: fields
1531 .remove("procid")
1532 .map(value_to_string)
1533 .map(|s| usize::from_str(s.as_str()).unwrap())
1534 .unwrap(),
1535 message: fields.remove("message").map(value_to_string).unwrap(),
1536 structured_data: structured_data_from_fields(fields),
1537 }
1538 }
1539 }
1540
1541 fn structured_data_from_fields(fields: ObjectMap) -> StructuredData {
1542 let mut structured_data = StructuredData::default();
1543
1544 for (key, value) in fields.into_iter() {
1545 let subfields = value
1546 .into_object()
1547 .unwrap()
1548 .into_iter()
1549 .map(|(k, v)| (k.into(), value_to_string(v)))
1550 .collect();
1551
1552 structured_data.insert(key.into(), subfields);
1553 }
1554
1555 structured_data
1556 }
1557
1558 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
1559 #[derive(Copy, Clone, Deserialize, PartialEq, Eq, Debug)]
1560 pub enum Severity {
1561 #[serde(rename(deserialize = "emergency"))]
1562 LOG_EMERG,
1563 #[serde(rename(deserialize = "alert"))]
1564 LOG_ALERT,
1565 #[serde(rename(deserialize = "critical"))]
1566 LOG_CRIT,
1567 #[serde(rename(deserialize = "error"))]
1568 LOG_ERR,
1569 #[serde(rename(deserialize = "warn"))]
1570 LOG_WARNING,
1571 #[serde(rename(deserialize = "notice"))]
1572 LOG_NOTICE,
1573 #[serde(rename(deserialize = "info"))]
1574 LOG_INFO,
1575 #[serde(rename(deserialize = "debug"))]
1576 LOG_DEBUG,
1577 }
1578
1579 impl Severity {
1580 fn from_str(s: &str) -> Option<Self> {
1581 match s {
1582 "emergency" => Some(Self::LOG_EMERG),
1583 "alert" => Some(Self::LOG_ALERT),
1584 "critical" => Some(Self::LOG_CRIT),
1585 "error" => Some(Self::LOG_ERR),
1586 "warn" => Some(Self::LOG_WARNING),
1587 "notice" => Some(Self::LOG_NOTICE),
1588 "info" => Some(Self::LOG_INFO),
1589 "debug" => Some(Self::LOG_DEBUG),
1590
1591 x => {
1592 #[allow(clippy::print_stdout)]
1593 {
1594 println!("converting severity str, got {x}");
1595 }
1596 None
1597 }
1598 }
1599 }
1600 }
1601
1602 #[allow(non_camel_case_types, clippy::upper_case_acronyms)]
1603 #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug)]
1604 pub enum Facility {
1605 #[serde(rename(deserialize = "kernel"))]
1606 LOG_KERN = 0 << 3,
1607 #[serde(rename(deserialize = "user"))]
1608 LOG_USER = 1 << 3,
1609 #[serde(rename(deserialize = "mail"))]
1610 LOG_MAIL = 2 << 3,
1611 #[serde(rename(deserialize = "daemon"))]
1612 LOG_DAEMON = 3 << 3,
1613 #[serde(rename(deserialize = "auth"))]
1614 LOG_AUTH = 4 << 3,
1615 #[serde(rename(deserialize = "syslog"))]
1616 LOG_SYSLOG = 5 << 3,
1617 }
1618
1619 impl Facility {
1620 fn from_str(s: &str) -> Option<Self> {
1621 match s {
1622 "kernel" => Some(Self::LOG_KERN),
1623 "user" => Some(Self::LOG_USER),
1624 "mail" => Some(Self::LOG_MAIL),
1625 "daemon" => Some(Self::LOG_DAEMON),
1626 "auth" => Some(Self::LOG_AUTH),
1627 "syslog" => Some(Self::LOG_SYSLOG),
1628 _ => None,
1629 }
1630 }
1631 }
1632
1633 type StructuredData = HashMap<String, HashMap<String, String>>;
1634
1635 fn random_structured_data(
1636 max_map_size: usize,
1637 max_children: usize,
1638 field_len: usize,
1639 ) -> StructuredData {
1640 let amount = rng().random_range(0..max_children);
1641
1642 random_maps(max_map_size, field_len)
1643 .filter(|m| !m.is_empty()) .take(amount)
1645 .enumerate()
1646 .map(|(i, map)| (format!("id{i}"), map))
1647 .collect()
1648 }
1649
1650 fn format_structured_data_rfc5424(data: &StructuredData) -> String {
1651 if data.is_empty() {
1652 "-".to_string()
1653 } else {
1654 let mut res = String::new();
1655 for (id, params) in data {
1656 res = res + "[" + id;
1657 for (name, value) in params {
1658 res = res + " " + name + "=\"" + value + "\"";
1659 }
1660 res += "]";
1661 }
1662
1663 res
1664 }
1665 }
1666
1667 const fn encode_priority(severity: Severity, facility: Facility) -> u8 {
1668 facility as u8 | severity as u8
1669 }
1670
1671 fn value_to_string(v: Value) -> String {
1672 if v.is_bytes() {
1673 let buf = v.as_bytes().unwrap();
1674 String::from_utf8_lossy(buf).to_string()
1675 } else if v.is_timestamp() {
1676 let ts = v.as_timestamp().unwrap();
1677 ts.to_rfc3339_opts(SecondsFormat::AutoSi, true)
1678 } else {
1679 v.to_string()
1680 }
1681 }
1682}