1use chrono::Utc;
2use futures::{StreamExt, stream};
3use vector_lib::{
4 codecs::BytesDeserializerConfig,
5 config::{LegacyKey, LogNamespace, log_schema},
6 configurable::configurable_component,
7 lookup::{OwnedValuePath, lookup_v2::OptionalValuePath, owned_value_path, path},
8 schema::Definition,
9};
10use vrl::value::Kind;
11
12use crate::{
13 SourceSender,
14 config::{DataType, SourceConfig, SourceContext, SourceOutput},
15 event::{EstimatedJsonEncodedSizeOf, Event},
16 internal_events::{InternalLogsBytesReceived, InternalLogsEventsReceived, StreamClosedError},
17 shutdown::ShutdownSignal,
18 trace::TraceSubscription,
19};
20
21#[configurable_component(source(
23 "internal_logs",
24 "Expose internal log messages emitted by the running Vector instance."
25))]
26#[derive(Clone, Debug)]
27#[serde(deny_unknown_fields)]
28pub struct InternalLogsConfig {
29 host_key: Option<OptionalValuePath>,
37
38 #[serde(default = "default_pid_key")]
44 pid_key: OptionalValuePath,
45
46 #[configurable(metadata(docs::hidden))]
48 #[serde(default)]
49 log_namespace: Option<bool>,
50}
51
52fn default_pid_key() -> OptionalValuePath {
53 OptionalValuePath::from(owned_value_path!("pid"))
54}
55
56impl_generate_config_from_default!(InternalLogsConfig);
57
58impl Default for InternalLogsConfig {
59 fn default() -> InternalLogsConfig {
60 InternalLogsConfig {
61 host_key: None,
62 pid_key: default_pid_key(),
63 log_namespace: None,
64 }
65 }
66}
67
68impl InternalLogsConfig {
69 fn schema_definition(&self, log_namespace: LogNamespace) -> Definition {
71 let host_key = self
72 .host_key
73 .clone()
74 .unwrap_or(log_schema().host_key().cloned().into())
75 .path
76 .map(LegacyKey::Overwrite);
77 let pid_key = self.pid_key.clone().path.map(LegacyKey::Overwrite);
78
79 BytesDeserializerConfig
82 .schema_definition(log_namespace)
83 .with_standard_vector_source_metadata()
84 .with_source_metadata(
85 InternalLogsConfig::NAME,
86 host_key,
87 &owned_value_path!("host"),
88 Kind::bytes().or_undefined(),
89 Some("host"),
90 )
91 .with_source_metadata(
92 InternalLogsConfig::NAME,
93 pid_key,
94 &owned_value_path!("pid"),
95 Kind::integer(),
96 None,
97 )
98 }
99}
100
101#[async_trait::async_trait]
102#[typetag::serde(name = "internal_logs")]
103impl SourceConfig for InternalLogsConfig {
104 async fn build(&self, cx: SourceContext) -> crate::Result<super::Source> {
105 let host_key = self
106 .host_key
107 .clone()
108 .unwrap_or(log_schema().host_key().cloned().into())
109 .path;
110 let pid_key = self.pid_key.clone().path;
111
112 let subscription = TraceSubscription::subscribe();
113
114 let log_namespace = cx.log_namespace(self.log_namespace);
115
116 Ok(Box::pin(run(
117 host_key,
118 pid_key,
119 subscription,
120 cx.out,
121 cx.shutdown,
122 log_namespace,
123 )))
124 }
125
126 fn outputs(&self, global_log_namespace: LogNamespace) -> Vec<SourceOutput> {
127 let schema_definition =
128 self.schema_definition(global_log_namespace.merge(self.log_namespace));
129
130 vec![SourceOutput::new_maybe_logs(
131 DataType::Log,
132 schema_definition,
133 )]
134 }
135
136 fn can_acknowledge(&self) -> bool {
137 false
138 }
139}
140
141async fn run(
142 host_key: Option<OwnedValuePath>,
143 pid_key: Option<OwnedValuePath>,
144 mut subscription: TraceSubscription,
145 mut out: SourceSender,
146 shutdown: ShutdownSignal,
147 log_namespace: LogNamespace,
148) -> Result<(), ()> {
149 let hostname = crate::get_hostname();
150 let pid = std::process::id();
151
152 let buffered_events = subscription.buffered_events().await;
155 let mut rx = stream::iter(buffered_events.into_iter().flatten())
156 .chain(subscription.into_stream())
157 .take_until(shutdown);
158
159 while let Some(mut log) = rx.next().await {
163 let byte_size = log.estimated_json_encoded_size_of().get();
165 let json_byte_size = log.estimated_json_encoded_size_of();
166 emit!(InternalLogsBytesReceived { byte_size });
168 emit!(InternalLogsEventsReceived {
169 count: 1,
170 byte_size: json_byte_size,
171 });
172
173 if let Ok(hostname) = &hostname {
174 let legacy_host_key = host_key.as_ref().map(LegacyKey::Overwrite);
175 log_namespace.insert_source_metadata(
176 InternalLogsConfig::NAME,
177 &mut log,
178 legacy_host_key,
179 path!("host"),
180 hostname.to_owned(),
181 );
182 }
183
184 let legacy_pid_key = pid_key.as_ref().map(LegacyKey::Overwrite);
185 log_namespace.insert_source_metadata(
186 InternalLogsConfig::NAME,
187 &mut log,
188 legacy_pid_key,
189 path!("pid"),
190 pid,
191 );
192
193 log_namespace.insert_standard_vector_source_metadata(
194 &mut log,
195 InternalLogsConfig::NAME,
196 Utc::now(),
197 );
198
199 if (out.send_event(Event::from(log)).await).is_err() {
200 emit!(StreamClosedError { count: 1 });
202 return Err(());
203 }
204 }
205
206 Ok(())
207}
208
209#[cfg(test)]
210mod tests {
211 use futures::Stream;
212 use tokio::time::{Duration, sleep};
213 use vector_lib::{SpanField, event::Value, lookup::OwnedTargetPath};
214 use vrl::value::kind::Collection;
215
216 use serial_test::serial;
217 use vrl::event_path;
218
219 use super::*;
220 use crate::{
221 event::Event,
222 test_util::{
223 collect_ready,
224 components::{SOURCE_TAGS, assert_source_compliance},
225 },
226 trace,
227 };
228
229 #[test]
230 fn generates_config() {
231 crate::test_util::test_generate_config::<InternalLogsConfig>();
232 }
233
234 #[tokio::test]
240 #[serial]
241 async fn receives_logs() {
242 trace::init(false, false, "debug", 10, None);
243 trace::reset_early_buffer();
244
245 assert_source_compliance(&SOURCE_TAGS, run_test()).await;
246 }
247
248 inventory::submit!(SpanField("component_new_field"));
250 inventory::submit!(SpanField("component_numerical_field"));
251
252 async fn run_test() {
253 let test_id: u8 = rand::random();
254 let start = chrono::Utc::now();
255
256 error!(message = "Before source started without span.", %test_id);
257
258 let span = error_span!(
259 "source",
260 component_kind = "source",
261 component_id = "foo",
262 component_type = "internal_logs",
263 );
264 let enter = span.enter();
265
266 error!(message = "Before source started.", %test_id);
267
268 drop(enter); let rx = start_source().await;
271
272 let enter = span.enter();
273
274 error!(message = "After source started.", %test_id);
275
276 {
277 let nested_span = error_span!(
278 "nested span",
279 component_kind = "bar",
280 component_new_field = "baz",
281 component_numerical_field = 1,
282 ignored_field = "foobarbaz",
283 );
284 let _enter = nested_span.enter();
285 error!(message = "In a nested span.", %test_id);
286 }
287
288 drop(enter);
289
290 sleep(Duration::from_millis(1)).await;
291 let mut events = collect_ready(rx).await;
292 let test_id = Value::from(test_id.to_string());
293 events.retain(|event| event.as_log().get(event_path!("test_id")) == Some(&test_id));
294
295 let end = chrono::Utc::now();
296
297 assert_eq!(events.len(), 4);
298
299 assert_eq!(
300 events[0].as_log()["message"],
301 "Before source started without span.".into()
302 );
303 assert_eq!(
304 events[1].as_log()["message"],
305 "Before source started.".into()
306 );
307 assert_eq!(
308 events[2].as_log()["message"],
309 "After source started.".into()
310 );
311 assert_eq!(events[3].as_log()["message"], "In a nested span.".into());
312
313 for (i, event) in events.iter().enumerate() {
314 let log = event.as_log();
315 let timestamp = *log["timestamp"]
316 .as_timestamp()
317 .expect("timestamp isn't a timestamp");
318 assert!(timestamp >= start);
319 assert!(timestamp <= end);
320 assert_eq!(log["metadata.kind"], "event".into());
321 assert_eq!(log["metadata.level"], "ERROR".into());
322 if i == 0 {
324 assert!(log.get(event_path!("vector", "component_id")).is_none());
325 assert!(log.get(event_path!("vector", "component_kind")).is_none());
326 assert!(log.get(event_path!("vector", "component_type")).is_none());
327 } else if i < 3 {
328 assert_eq!(log["vector.component_id"], "foo".into());
329 assert_eq!(log["vector.component_kind"], "source".into());
330 assert_eq!(log["vector.component_type"], "internal_logs".into());
331 } else {
332 assert_eq!(log["vector.component_id"], "foo".into());
336 assert_eq!(log["vector.component_kind"], "bar".into());
337 assert_eq!(log["vector.component_type"], "internal_logs".into());
338 assert_eq!(log["vector.component_new_field"], "baz".into());
339 assert_eq!(log["vector.component_numerical_field"], 1.into());
340 assert!(log.get(event_path!("vector", "ignored_field")).is_none());
341 }
342 }
343 }
344
345 async fn start_source() -> impl Stream<Item = Event> + Unpin {
346 let (tx, rx) = SourceSender::new_test();
347
348 let source = InternalLogsConfig::default()
349 .build(SourceContext::new_test(tx, None))
350 .await
351 .unwrap();
352 tokio::spawn(source);
353 sleep(Duration::from_millis(1)).await;
354 trace::stop_early_buffering();
355 rx
356 }
357
358 vector_lib::register_extra_span_field!("internal_logs_test_extra_field");
363
364 #[tokio::test]
365 #[serial]
366 async fn registered_extra_span_field_is_captured() {
367 trace::init(false, false, "info", 10, None);
368 trace::reset_early_buffer();
369
370 let test_id: u8 = rand::random();
371 let rx = start_source().await;
372
373 {
374 let span = error_span!(
375 "extras",
376 component_id = "foo",
377 internal_logs_test_extra_field = "captured",
378 some_other_field = "dropped",
379 );
380 let _enter = span.enter();
381 error!(message = "With extra field.", %test_id);
382 }
383
384 sleep(Duration::from_millis(1)).await;
385 let mut events = collect_ready(rx).await;
386 let test_id_value = Value::from(test_id.to_string());
387 events.retain(|event| event.as_log().get(event_path!("test_id")) == Some(&test_id_value));
388
389 assert_eq!(events.len(), 1);
390 let log = events[0].as_log();
391 assert_eq!(
392 log["vector.internal_logs_test_extra_field"],
393 "captured".into()
394 );
395 assert!(log.get(event_path!("vector", "some_other_field")).is_none());
397 }
398
399 #[tokio::test]
402 #[serial]
403 async fn repeated_logs_are_not_rate_limited() {
404 trace::init(false, false, "info", 10, None);
405 trace::reset_early_buffer();
406
407 let rx = start_source().await;
408
409 for _ in 0..20 {
411 info!(component_id = "test", "Repeated test message.");
412 }
413
414 sleep(Duration::from_millis(50)).await;
415 let events = collect_ready(rx).await;
416
417 let test_events: Vec<_> = events
419 .iter()
420 .filter(|e| {
421 e.as_log()
422 .get(event_path!("message"))
423 .map(|m| m.to_string_lossy() == "Repeated test message.")
424 .unwrap_or(false)
425 })
426 .collect();
427
428 assert_eq!(
430 test_events.len(),
431 20,
432 "internal_logs source should capture all repeated messages without rate limiting"
433 );
434 }
435
436 #[test]
437 fn output_schema_definition_vector_namespace() {
438 let config = InternalLogsConfig::default();
439
440 let definitions = config
441 .outputs(LogNamespace::Vector)
442 .remove(0)
443 .schema_definition(true);
444
445 let expected_definition =
446 Definition::new_with_default_metadata(Kind::bytes(), [LogNamespace::Vector])
447 .with_meaning(OwnedTargetPath::event_root(), "message")
448 .with_metadata_field(
449 &owned_value_path!("vector", "source_type"),
450 Kind::bytes(),
451 None,
452 )
453 .with_metadata_field(
454 &owned_value_path!(InternalLogsConfig::NAME, "pid"),
455 Kind::integer(),
456 None,
457 )
458 .with_metadata_field(
459 &owned_value_path!("vector", "ingest_timestamp"),
460 Kind::timestamp(),
461 None,
462 )
463 .with_metadata_field(
464 &owned_value_path!(InternalLogsConfig::NAME, "host"),
465 Kind::bytes().or_undefined(),
466 Some("host"),
467 );
468
469 assert_eq!(definitions, Some(expected_definition))
470 }
471
472 #[test]
473 fn output_schema_definition_legacy_namespace() {
474 let mut config = InternalLogsConfig::default();
475
476 let pid_key = "pid_a_pid_a_pid_pid_pid";
477
478 config.pid_key = OptionalValuePath::from(owned_value_path!(pid_key));
479
480 let definitions = config
481 .outputs(LogNamespace::Legacy)
482 .remove(0)
483 .schema_definition(true);
484
485 let expected_definition = Definition::new_with_default_metadata(
486 Kind::object(Collection::empty()),
487 [LogNamespace::Legacy],
488 )
489 .with_event_field(
490 &owned_value_path!("message"),
491 Kind::bytes(),
492 Some("message"),
493 )
494 .with_event_field(&owned_value_path!("source_type"), Kind::bytes(), None)
495 .with_event_field(&owned_value_path!(pid_key), Kind::integer(), None)
496 .with_event_field(&owned_value_path!("timestamp"), Kind::timestamp(), None)
497 .with_event_field(
498 &owned_value_path!("host"),
499 Kind::bytes().or_undefined(),
500 Some("host"),
501 );
502
503 assert_eq!(definitions, Some(expected_definition))
504 }
505}