1use bytes::{BufMut, BytesMut};
2use chrono::{DateTime, SecondsFormat, SubsecRound, Utc};
3use lookup::lookup_v2::ConfigTargetPath;
4use std::collections::HashMap;
5use std::fmt::Write;
6use std::str::FromStr;
7use strum::{EnumString, FromRepr, VariantNames};
8use tokio_util::codec::Encoder;
9use vector_config::configurable_component;
10use vector_core::{
11 config::DataType,
12 event::{Event, LogEvent, Value},
13 schema,
14};
15use vrl::value::ObjectMap;
16
17#[configurable_component]
19#[derive(Clone, Debug, Default)]
20#[serde(default)]
21pub struct SyslogSerializerConfig {
22 pub syslog: SyslogSerializerOptions,
24}
25
26impl SyslogSerializerConfig {
27 pub fn build(&self) -> SyslogSerializer {
29 SyslogSerializer::new(self)
30 }
31
32 pub fn input_type(&self) -> DataType {
34 DataType::Log
35 }
36
37 pub fn schema_requirement(&self) -> schema::Requirement {
39 schema::Requirement::empty()
40 }
41}
42
43#[configurable_component]
45#[derive(Clone, Debug, Default)]
46#[serde(default, deny_unknown_fields)]
47pub struct SyslogSerializerOptions {
48 rfc: SyslogRFC,
50 facility: Option<ConfigTargetPath>,
52 severity: Option<ConfigTargetPath>,
54 app_name: Option<ConfigTargetPath>,
59 proc_id: Option<ConfigTargetPath>,
61 msg_id: Option<ConfigTargetPath>,
63}
64
65#[derive(Debug, Clone)]
67pub struct SyslogSerializer {
68 config: SyslogSerializerConfig,
69}
70
71impl SyslogSerializer {
72 pub fn new(conf: &SyslogSerializerConfig) -> Self {
74 Self {
75 config: conf.clone(),
76 }
77 }
78}
79
80impl Encoder<Event> for SyslogSerializer {
81 type Error = vector_common::Error;
82
83 fn encode(&mut self, event: Event, buffer: &mut BytesMut) -> Result<(), Self::Error> {
84 if let Event::Log(log_event) = event {
85 let syslog_message = ConfigDecanter::new(&log_event).decant_config(&self.config.syslog);
86 let vec = syslog_message
87 .encode(&self.config.syslog.rfc)
88 .as_bytes()
89 .to_vec();
90 buffer.put_slice(&vec);
91 }
92
93 Ok(())
94 }
95}
96
97struct ConfigDecanter<'a> {
98 log: &'a LogEvent,
99}
100
101impl<'a> ConfigDecanter<'a> {
102 fn new(log: &'a LogEvent) -> Self {
103 Self { log }
104 }
105
106 fn decant_config(&self, config: &SyslogSerializerOptions) -> SyslogMessage {
107 let mut app_name = self
108 .get_value(&config.app_name) .unwrap_or_else(|| {
110 self.log
112 .get_by_meaning("service")
113 .map(|v| v.to_string_lossy().to_string())
114 .unwrap_or_else(|| "vector".to_owned())
116 });
117 let mut proc_id = self.get_value(&config.proc_id);
118 let mut msg_id = self.get_value(&config.msg_id);
119 if config.rfc == SyslogRFC::Rfc5424 {
120 if app_name.len() > 48 {
121 app_name.truncate(48);
122 }
123 if let Some(pid) = &mut proc_id
124 && pid.len() > 128
125 {
126 pid.truncate(128);
127 }
128 if let Some(mid) = &mut msg_id
129 && mid.len() > 32
130 {
131 mid.truncate(32);
132 }
133 }
134
135 SyslogMessage {
136 pri: Pri {
137 facility: self.get_facility(config),
138 severity: self.get_severity(config),
139 },
140 timestamp: self.get_timestamp(),
141 hostname: self.log.get_host().map(|v| v.to_string_lossy().to_string()),
142 tag: Tag {
143 app_name,
144 proc_id,
145 msg_id,
146 },
147 structured_data: self.get_structured_data(),
148 message: self.get_payload(),
149 }
150 }
151
152 fn get_value(&self, path: &Option<ConfigTargetPath>) -> Option<String> {
153 path.as_ref()
154 .and_then(|p| self.log.get(p).cloned())
155 .map(|v| v.to_string_lossy().to_string())
156 }
157
158 fn get_structured_data(&self) -> Option<StructuredData> {
159 self.log
160 .get("structured_data")
161 .and_then(|v| v.clone().into_object())
162 .map(StructuredData::from)
163 }
164
165 fn get_timestamp(&self) -> DateTime<Utc> {
166 if let Some(Value::Timestamp(timestamp)) = self.log.get_timestamp() {
167 return *timestamp;
168 }
169 Utc::now()
170 }
171
172 fn get_payload(&self) -> String {
173 self.log
174 .get_message()
175 .map(|v| v.to_string_lossy().to_string())
176 .unwrap_or_default()
177 }
178
179 fn get_facility(&self, config: &SyslogSerializerOptions) -> Facility {
180 config.facility.as_ref().map_or(Facility::User, |path| {
181 self.get_syslog_code(path, Facility::from_repr, Facility::User)
182 })
183 }
184
185 fn get_severity(&self, config: &SyslogSerializerOptions) -> Severity {
186 config
187 .severity
188 .as_ref()
189 .map_or(Severity::Informational, |path| {
190 self.get_syslog_code(path, Severity::from_repr, Severity::Informational)
191 })
192 }
193
194 fn get_syslog_code<T>(
195 &self,
196 path: &ConfigTargetPath,
197 from_repr_fn: fn(usize) -> Option<T>,
198 default_value: T,
199 ) -> T
200 where
201 T: Copy + FromStr,
202 {
203 if let Some(value) = self.log.get(path).cloned() {
204 let s = value.to_string_lossy();
205 if let Ok(val_from_name) = s.to_ascii_lowercase().parse::<T>() {
206 return val_from_name;
207 }
208 if let Value::Integer(n) = value
209 && let Some(val_from_num) = from_repr_fn(n as usize)
210 {
211 return val_from_num;
212 }
213 }
214 default_value
215 }
216}
217
218const NIL_VALUE: &str = "-";
219const SYSLOG_V1: &str = "1";
220const RFC3164_TAG_MAX_LENGTH: usize = 32;
221
222#[configurable_component]
224#[derive(PartialEq, Clone, Debug, Default)]
225#[serde(rename_all = "snake_case")]
226pub enum SyslogRFC {
227 Rfc3164,
229 #[default]
231 Rfc5424,
232}
233
234#[derive(Default, Debug)]
235struct SyslogMessage {
236 pri: Pri,
237 timestamp: DateTime<Utc>,
238 hostname: Option<String>,
239 tag: Tag,
240 structured_data: Option<StructuredData>,
241 message: String,
242}
243
244impl SyslogMessage {
245 fn encode(&self, rfc: &SyslogRFC) -> String {
246 let pri_header = self.pri.encode();
247
248 let mut parts = Vec::new();
249
250 let timestamp_str = match rfc {
251 SyslogRFC::Rfc3164 => self.timestamp.format("%b %e %H:%M:%S").to_string(),
252 SyslogRFC::Rfc5424 => self
253 .timestamp
254 .round_subsecs(6)
255 .to_rfc3339_opts(SecondsFormat::Micros, true),
256 };
257 parts.push(timestamp_str);
258 parts.push(self.hostname.as_deref().unwrap_or(NIL_VALUE).to_string());
259
260 let tag_str = match rfc {
261 SyslogRFC::Rfc3164 => self.tag.encode_rfc_3164(),
262 SyslogRFC::Rfc5424 => self.tag.encode_rfc_5424(),
263 };
264 parts.push(tag_str);
265
266 let mut message_part = self.message.clone();
267 if *rfc == SyslogRFC::Rfc3164 {
268 message_part = Self::sanitize_rfc3164_message(&message_part);
269 }
270
271 if let Some(sd) = &self.structured_data {
272 let sd_string = sd.encode();
273 if *rfc == SyslogRFC::Rfc3164 {
274 if !sd.elements.is_empty() {
275 if !message_part.is_empty() {
276 message_part = format!("{sd_string} {message_part}");
277 } else {
278 message_part = sd_string;
279 }
280 }
281 } else {
282 parts.push(sd_string);
283 }
284 } else if *rfc == SyslogRFC::Rfc5424 {
285 parts.push(NIL_VALUE.to_string());
286 }
287
288 if !message_part.is_empty() {
289 parts.push(message_part);
290 }
291
292 let main_message = parts.join(" ");
293
294 if *rfc == SyslogRFC::Rfc5424 {
295 format!("{pri_header}{SYSLOG_V1} {main_message}")
296 } else {
297 format!("{pri_header}{main_message}")
298 }
299 }
300
301 fn sanitize_rfc3164_message(message: &str) -> String {
302 message
303 .chars()
304 .map(|ch| if (' '..='~').contains(&ch) { ch } else { ' ' })
305 .collect()
306 }
307}
308
309#[derive(Default, Debug)]
310struct Tag {
311 app_name: String,
312 proc_id: Option<String>,
313 msg_id: Option<String>,
314}
315
316impl Tag {
317 fn encode_rfc_3164(&self) -> String {
318 let mut tag = if let Some(proc_id) = self.proc_id.as_deref() {
319 format!("{}[{}]:", self.app_name, proc_id)
320 } else {
321 format!("{}:", self.app_name)
322 };
323 if tag.len() > RFC3164_TAG_MAX_LENGTH {
324 tag.truncate(RFC3164_TAG_MAX_LENGTH);
325 if !tag.ends_with(':') {
326 tag.pop();
327 tag.push(':');
328 }
329 }
330 tag
331 }
332
333 fn encode_rfc_5424(&self) -> String {
334 let proc_id_str = self.proc_id.as_deref().unwrap_or(NIL_VALUE);
335 let msg_id_str = self.msg_id.as_deref().unwrap_or(NIL_VALUE);
336 format!("{} {} {}", self.app_name, proc_id_str, msg_id_str)
337 }
338}
339
340type StructuredDataMap = HashMap<String, HashMap<String, String>>;
341#[derive(Debug, Default)]
342struct StructuredData {
343 elements: StructuredDataMap,
344}
345
346impl StructuredData {
347 fn encode(&self) -> String {
348 if self.elements.is_empty() {
349 NIL_VALUE.to_string()
350 } else {
351 self.elements
352 .iter()
353 .fold(String::new(), |mut acc, (sd_id, sd_params)| {
354 let _ = write!(acc, "[{sd_id}");
355 for (key, value) in sd_params {
356 let esc_val = Self::escape_sd(value);
357 let _ = write!(acc, " {key}=\"{esc_val}\"");
358 }
359 let _ = write!(acc, "]");
360 acc
361 })
362 }
363 }
364
365 fn escape_sd(s: &str) -> String {
366 s.replace('\\', "\\\\")
367 .replace('"', "\\\"")
368 .replace(']', "\\]")
369 }
370}
371
372impl From<ObjectMap> for StructuredData {
373 fn from(fields: ObjectMap) -> Self {
374 let elements = fields
375 .into_iter()
376 .flat_map(|(sd_id, value)| {
377 let sd_params = value
378 .into_object()?
379 .into_iter()
380 .map(|(k, v)| (k.into(), v.to_string_lossy().to_string()))
381 .collect();
382 Some((sd_id.into(), sd_params))
383 })
384 .collect();
385 Self { elements }
386 }
387}
388
389#[derive(Default, Debug)]
390struct Pri {
391 facility: Facility,
392 severity: Severity,
393}
394
395impl Pri {
396 fn encode(&self) -> String {
399 let pri_val = (self.facility as u8 * 8) + self.severity as u8;
400 format!("<{pri_val}>")
401 }
402}
403
404#[derive(Default, Debug, EnumString, FromRepr, VariantNames, Copy, Clone, PartialEq, Eq)]
406#[strum(serialize_all = "kebab-case")]
407#[configurable_component]
408pub enum Facility {
409 Kern = 0,
411 #[default]
413 User = 1,
414 Mail = 2,
416 Daemon = 3,
418 Auth = 4,
420 Syslog = 5,
422 Lpr = 6,
424 News = 7,
426 Uucp = 8,
428 Cron = 9,
430 Authpriv = 10,
432 Ftp = 11,
434 Ntp = 12,
436 Security = 13,
438 Console = 14,
440 SolarisCron = 15,
442 Local0 = 16,
444 Local1 = 17,
446 Local2 = 18,
448 Local3 = 19,
450 Local4 = 20,
452 Local5 = 21,
454 Local6 = 22,
456 Local7 = 23,
458}
459
460#[derive(Default, Debug, EnumString, FromRepr, VariantNames, Copy, Clone, PartialEq, Eq)]
462#[strum(serialize_all = "kebab-case")]
463#[configurable_component]
464pub enum Severity {
465 Emergency = 0,
467 Alert = 1,
469 Critical = 2,
471 Error = 3,
473 Warning = 4,
475 Notice = 5,
477 #[default]
479 Informational = 6,
480 Debug = 7,
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487 use bytes::BytesMut;
488 use chrono::NaiveDate;
489 use std::sync::Arc;
490 use vector_core::config::LogNamespace;
491 use vector_core::event::Event::Metric;
492 use vector_core::event::{Event, MetricKind, MetricValue, StatisticKind};
493 use vrl::path::parse_target_path;
494 use vrl::prelude::Kind;
495 use vrl::{btreemap, event_path, value};
496
497 fn run_encode(config: SyslogSerializerConfig, event: Event) -> String {
498 let mut serializer = SyslogSerializer::new(&config);
499 let mut buffer = BytesMut::new();
500 serializer.encode(event, &mut buffer).unwrap();
501 String::from_utf8(buffer.to_vec()).unwrap()
502 }
503
504 fn create_simple_log() -> LogEvent {
505 let mut log = LogEvent::from("original message");
506 log.insert(
507 event_path!("timestamp"),
508 NaiveDate::from_ymd_opt(2025, 8, 28)
509 .unwrap()
510 .and_hms_micro_opt(18, 30, 00, 123456)
511 .unwrap()
512 .and_local_timezone(Utc)
513 .unwrap(),
514 );
515 log.insert(event_path!("host"), "test-host.com");
516 log
517 }
518
519 fn create_test_log() -> LogEvent {
520 let mut log = create_simple_log();
521 log.insert(event_path!("app"), "my-app");
522 log.insert(event_path!("pid"), "12345");
523 log.insert(event_path!("mid"), "req-abc-789");
524 log.insert(event_path!("fac"), "daemon"); log.insert(event_path!("sev"), Value::from(2u8)); log.insert(
527 event_path!("structured_data"),
528 value!({"metrics": {"retries": 3}}),
529 );
530 log
531 }
532
533 #[test]
534 fn test_rfc5424_defaults() {
535 let config = toml::from_str::<SyslogSerializerConfig>(
536 r#"
537 [syslog]
538 rfc = "rfc5424"
539 "#,
540 )
541 .unwrap();
542 let log = create_simple_log();
543 let output = run_encode(config, Event::Log(log));
544 let expected =
545 "<14>1 2025-08-28T18:30:00.123456Z test-host.com vector - - - original message";
546 assert_eq!(output, expected);
547 }
548
549 #[test]
550 fn test_rfc5424_all_fields() {
551 let config = toml::from_str::<SyslogSerializerConfig>(
552 r#"
553 [syslog]
554 app_name = ".app"
555 proc_id = ".pid"
556 msg_id = ".mid"
557 facility = ".fac"
558 severity = ".sev"
559 "#,
560 )
561 .unwrap();
562 let log = create_test_log();
563 let output = run_encode(config, Event::Log(log));
564 let expected = "<26>1 2025-08-28T18:30:00.123456Z test-host.com my-app 12345 req-abc-789 [metrics retries=\"3\"] original message";
565 assert_eq!(output, expected);
566 }
567
568 #[test]
569 fn test_rfc3164_all_fields() {
570 let config = toml::from_str::<SyslogSerializerConfig>(
571 r#"
572 [syslog]
573 rfc = "rfc3164"
574 facility = ".fac"
575 severity = ".sev"
576 app_name = ".app"
577 proc_id = ".pid"
578 "#,
579 )
580 .unwrap();
581 let log = create_test_log();
582 let output = run_encode(config, Event::Log(log));
583 let expected = "<26>Aug 28 18:30:00 test-host.com my-app[12345]: [metrics retries=\"3\"] original message";
584 assert_eq!(output, expected);
585 }
586
587 #[test]
588 fn test_parsing_logic() {
589 let mut log = LogEvent::from("test message");
590 let config_fac =
591 toml::from_str::<SyslogSerializerOptions>(r#"facility = ".syslog_facility""#).unwrap();
592 let config_sev =
593 toml::from_str::<SyslogSerializerOptions>(r#"severity = ".syslog_severity""#).unwrap();
594 log.insert(event_path!("syslog_facility"), "daemon");
596 log.insert(event_path!("syslog_severity"), "critical");
597 let decanter = ConfigDecanter::new(&log);
598 let facility = decanter.get_facility(&config_fac);
599 let severity = decanter.get_severity(&config_sev);
600 assert_eq!(facility, Facility::Daemon);
601 assert_eq!(severity, Severity::Critical);
602
603 log.insert(event_path!("syslog_facility"), "DAEMON");
605 log.insert(event_path!("syslog_severity"), "CRITICAL");
606 let decanter = ConfigDecanter::new(&log);
607 let facility = decanter.get_facility(&config_fac);
608 let severity = decanter.get_severity(&config_sev);
609 assert_eq!(facility, Facility::Daemon);
610 assert_eq!(severity, Severity::Critical);
611
612 log.insert(event_path!("syslog_facility"), Value::from(3u8));
614 log.insert(event_path!("syslog_severity"), Value::from(2u8));
615 let decanter = ConfigDecanter::new(&log);
616 let facility = decanter.get_facility(&config_fac);
617 let severity = decanter.get_severity(&config_sev);
618 assert_eq!(facility, Facility::Daemon);
619 assert_eq!(severity, Severity::Critical);
620
621 let empty_config =
623 toml::from_str::<SyslogSerializerOptions>(r#"facility = ".missing_field""#).unwrap();
624 let default_facility = decanter.get_facility(&empty_config);
625 let default_severity = decanter.get_severity(&empty_config);
626 assert_eq!(default_facility, Facility::User);
627 assert_eq!(default_severity, Severity::Informational);
628 }
629
630 #[test]
631 fn test_rfc3164_sanitization() {
632 let config = toml::from_str::<SyslogSerializerConfig>(
633 r#"
634 [syslog]
635 rfc = "rfc3164"
636 "#,
637 )
638 .unwrap();
639
640 let mut log = create_simple_log();
641 log.insert(
642 event_path!("message"),
643 "A\nB\tC, Привіт D, E\u{0007}F", );
645
646 let output = run_encode(config, Event::Log(log));
647 let expected_message = "A B C, D, E F";
648 assert!(output.ends_with(expected_message));
649 }
650
651 #[test]
652 fn test_rfc5424_field_truncation() {
653 let long_string = "vector".repeat(50);
654
655 let mut log = create_simple_log();
656 log.insert(event_path!("long_app_name"), long_string.clone());
657 log.insert(event_path!("long_proc_id"), long_string.clone());
658 log.insert(event_path!("long_msg_id"), long_string.clone());
659
660 let config = toml::from_str::<SyslogSerializerConfig>(
661 r#"
662 [syslog]
663 rfc = "rfc5424"
664 app_name = ".long_app_name"
665 proc_id = ".long_proc_id"
666 msg_id = ".long_msg_id"
667 "#,
668 )
669 .unwrap();
670
671 let decanter = ConfigDecanter::new(&log);
672 let message = decanter.decant_config(&config.syslog);
673
674 assert_eq!(message.tag.app_name.len(), 48);
675 assert_eq!(message.tag.proc_id.unwrap().len(), 128);
676 assert_eq!(message.tag.msg_id.unwrap().len(), 32);
677 }
678
679 #[test]
680 fn test_rfc3164_tag_truncation() {
681 let config = toml::from_str::<SyslogSerializerConfig>(
682 r#"
683 [syslog]
684 rfc = "rfc3164"
685 facility = "user"
686 severity = "notice"
687 app_name = ".app_name"
688 proc_id = ".proc_id"
689 "#,
690 )
691 .unwrap();
692
693 let mut log = create_simple_log();
694 log.insert(
695 event_path!("app_name"),
696 "this-is-a-very-very-long-application-name",
697 );
698 log.insert(event_path!("proc_id"), "1234567890");
699
700 let output = run_encode(config, Event::Log(log));
701 let expected_tag = "this-is-a-very-very-long-applic:";
702 assert!(output.contains(expected_tag));
703 }
704
705 #[test]
706 fn test_rfc5424_missing_fields() {
707 let config = toml::from_str::<SyslogSerializerConfig>(
708 r#"
709 [syslog]
710 rfc = "rfc5424"
711 app_name = ".app" # configured path, but not in log
712 proc_id = ".pid" # configured path, but not in log
713 msg_id = ".mid" # configured path, but not in log
714 "#,
715 )
716 .unwrap();
717
718 let log = create_simple_log();
719 let output = run_encode(config, Event::Log(log));
720
721 let expected =
722 "<14>1 2025-08-28T18:30:00.123456Z test-host.com vector - - - original message";
723 assert_eq!(output, expected);
724 }
725
726 #[test]
727 fn test_invalid_parsing_fallback() {
728 let config = toml::from_str::<SyslogSerializerConfig>(
729 r#"
730 [syslog]
731 rfc = "rfc5424"
732 facility = ".fac"
733 severity = ".sev"
734 "#,
735 )
736 .unwrap();
737
738 let mut log = create_simple_log();
739
740 log.insert(event_path!("fac"), "");
741 log.insert(event_path!("sev"), "invalid_severity_name");
742
743 let output = run_encode(config, Event::Log(log));
744
745 let expected_pri = "<14>";
746 assert!(output.starts_with(expected_pri));
747
748 let expected_suffix = "vector - - - original message";
749 assert!(output.ends_with(expected_suffix));
750 }
751
752 #[test]
753 fn test_rfc5424_empty_message_and_sd() {
754 let config = toml::from_str::<SyslogSerializerConfig>(
755 r#"
756 [syslog]
757 rfc = "rfc5424"
758 app_name = ".app"
759 proc_id = ".pid"
760 msg_id = ".mid"
761 "#,
762 )
763 .unwrap();
764
765 let mut log = create_simple_log();
766 log.insert(event_path!("message"), "");
767 log.insert(event_path!("structured_data"), value!({}));
768
769 let output = run_encode(config, Event::Log(log));
770 let expected = "<14>1 2025-08-28T18:30:00.123456Z test-host.com vector - - -";
771 assert_eq!(output, expected);
772 }
773
774 #[test]
775 fn test_non_log_event_filtering() {
776 let config = toml::from_str::<SyslogSerializerConfig>(
777 r#"
778 [syslog]
779 rfc = "rfc5424"
780 "#,
781 )
782 .unwrap();
783
784 let metric_event = Metric(vector_core::event::Metric::new(
785 "metric1",
786 MetricKind::Incremental,
787 MetricValue::Distribution {
788 samples: vector_core::samples![10.0 => 1],
789 statistic: StatisticKind::Histogram,
790 },
791 ));
792
793 let mut serializer = SyslogSerializer::new(&config);
794 let mut buffer = BytesMut::new();
795
796 let result = serializer.encode(metric_event, &mut buffer);
797
798 assert!(result.is_ok());
799 assert!(buffer.is_empty());
800 }
801
802 #[test]
803 fn test_minimal_event() {
804 let config = toml::from_str::<SyslogSerializerConfig>(
805 r#"
806 [syslog]
807 "#,
808 )
809 .unwrap();
810 let log = LogEvent::from("");
811
812 let output = run_encode(config, Event::Log(log));
813 let expected_suffix = "vector - - -";
814 assert!(output.starts_with("<14>1"));
815 assert!(output.ends_with(expected_suffix));
816 }
817
818 #[test]
819 fn test_app_name_meaning_fallback() {
820 let config = toml::from_str::<SyslogSerializerConfig>(
821 r#"
822 [syslog]
823 rfc = "rfc5424"
824 severity = ".sev"
825 app_name = ".nonexistent"
826 "#,
827 )
828 .unwrap();
829
830 let mut log = LogEvent::default();
831 log.insert("syslog.service", "meaning-app");
832
833 let schema = schema::Definition::new_with_default_metadata(
834 Kind::object(btreemap! {
835 "syslog" => Kind::object(btreemap! {
836 "service" => Kind::bytes(),
837 })
838 }),
839 [LogNamespace::Vector],
840 );
841 let schema = schema.with_meaning(parse_target_path("syslog.service").unwrap(), "service");
842 let mut event = Event::from(log);
843 event
844 .metadata_mut()
845 .set_schema_definition(&Arc::new(schema));
846
847 let output = run_encode(config, event);
848 assert!(output.contains("meaning-app - -"));
849 }
850}