Skip to main content

vector/internal_events/
file.rs

1#![allow(dead_code)] // TODO requires optional feature compilation
2
3use std::borrow::Cow;
4
5use vector_lib::{
6    NamedInternalEvent,
7    configurable::configurable_component,
8    counter, gauge,
9    internal_event::{
10        ComponentEventsDropped, CounterName, GaugeName, INTENTIONAL, InternalEvent, UNINTENTIONAL,
11        error_stage, error_type,
12    },
13};
14
15use crate::sinks::util::path_confinement::ConfineError;
16
17#[cfg(any(feature = "sources-file", feature = "sources-kubernetes_logs"))]
18pub use self::source::*;
19
20/// Configuration of internal metrics for file-based components.
21#[configurable_component]
22#[derive(Clone, Debug, PartialEq, Eq, Default)]
23#[serde(deny_unknown_fields)]
24pub struct FileInternalMetricsConfig {
25    /// Whether or not to include the "file" tag on the component's corresponding internal metrics.
26    ///
27    /// This is useful for distinguishing between different files while monitoring. However, the tag's
28    /// cardinality is unbounded.
29    #[serde(default = "crate::serde::default_false")]
30    pub include_file_tag: bool,
31}
32
33#[derive(Debug, NamedInternalEvent)]
34pub struct FileOpen {
35    pub count: usize,
36}
37
38impl InternalEvent for FileOpen {
39    fn emit(self) {
40        gauge!(GaugeName::OpenFiles).set(self.count as f64);
41    }
42}
43
44#[derive(Debug, NamedInternalEvent)]
45pub struct FileBytesSent<'a> {
46    pub byte_size: usize,
47    pub file: Cow<'a, str>,
48    pub include_file_metric_tag: bool,
49}
50
51impl InternalEvent for FileBytesSent<'_> {
52    fn emit(self) {
53        trace!(
54            message = "Bytes sent.",
55            byte_size = %self.byte_size,
56            protocol = "file",
57            file = %self.file,
58        );
59        if self.include_file_metric_tag {
60            counter!(
61                CounterName::ComponentSentBytesTotal,
62                "protocol" => "file",
63                "file" => self.file.clone().into_owned(),
64            )
65        } else {
66            counter!(
67                CounterName::ComponentSentBytesTotal,
68                "protocol" => "file",
69            )
70        }
71        .increment(self.byte_size as u64);
72    }
73}
74
75#[derive(Debug, NamedInternalEvent)]
76pub struct FileIoError<'a, P> {
77    pub error: std::io::Error,
78    pub code: &'static str,
79    pub message: &'static str,
80    pub path: &'a P,
81    pub dropped_events: usize,
82}
83
84impl<P: std::fmt::Debug> InternalEvent for FileIoError<'_, P> {
85    fn emit(self) {
86        error!(
87            message = %self.message,
88            path = ?self.path,
89            error = %self.error,
90            error_code = %self.code,
91            error_type = error_type::IO_FAILED,
92            stage = error_stage::SENDING,
93        );
94        counter!(
95            CounterName::ComponentErrorsTotal,
96            "error_code" => self.code,
97            "error_type" => error_type::IO_FAILED,
98            "stage" => error_stage::SENDING,
99        )
100        .increment(1);
101
102        if self.dropped_events > 0 {
103            emit!(ComponentEventsDropped::<UNINTENTIONAL> {
104                count: self.dropped_events,
105                reason: self.message,
106            });
107        }
108    }
109}
110
111#[derive(Debug, NamedInternalEvent)]
112pub struct FilePathOutsideBaseDirError<'a> {
113    pub path: &'a std::path::Path,
114    pub base_dir: &'a std::path::Path,
115    pub error: ConfineError,
116}
117
118impl InternalEvent for FilePathOutsideBaseDirError<'_> {
119    fn emit(self) {
120        error!(
121            message = "Rendered path is outside the configured base directory; dropping event.",
122            path = ?self.path,
123            base_dir = ?self.base_dir,
124            error = %self.error,
125            error_type = error_type::CONFINEMENT_FAILED,
126            stage = error_stage::PROCESSING,
127        );
128        counter!(
129            CounterName::ComponentErrorsTotal,
130            "error_type" => error_type::CONFINEMENT_FAILED,
131            "stage" => error_stage::PROCESSING,
132        )
133        .increment(1);
134        emit!(ComponentEventsDropped::<INTENTIONAL> {
135            count: 1,
136            reason: "Rendered path outside base_dir.",
137        });
138    }
139}
140
141#[cfg(any(feature = "sources-file", feature = "sources-kubernetes_logs"))]
142mod source {
143    use std::{io::Error, path::Path, time::Duration};
144
145    use bytes::BytesMut;
146    use vector_lib::{
147        NamedInternalEvent, counter, emit,
148        file_source_common::internal_events::FileSourceInternalEvents,
149        internal_event::{
150            ComponentEventsDropped, CounterName, INTENTIONAL, error_stage, error_type,
151        },
152        json_size::JsonSize,
153    };
154
155    use super::{FileOpen, InternalEvent};
156
157    #[derive(Debug, NamedInternalEvent)]
158    pub struct FileBytesReceived<'a> {
159        pub byte_size: usize,
160        pub file: &'a str,
161        pub include_file_metric_tag: bool,
162    }
163
164    impl InternalEvent for FileBytesReceived<'_> {
165        fn emit(self) {
166            trace!(
167                message = "Bytes received.",
168                byte_size = %self.byte_size,
169                protocol = "file",
170                file = %self.file,
171            );
172            if self.include_file_metric_tag {
173                counter!(
174                    CounterName::ComponentReceivedBytesTotal,
175                    "protocol" => "file",
176                    "file" => self.file.to_owned()
177                )
178            } else {
179                counter!(
180                    CounterName::ComponentReceivedBytesTotal,
181                    "protocol" => "file",
182                )
183            }
184            .increment(self.byte_size as u64);
185        }
186    }
187
188    #[derive(Debug, NamedInternalEvent)]
189    pub struct FileEventsReceived<'a> {
190        pub count: usize,
191        pub file: &'a str,
192        pub byte_size: JsonSize,
193        pub include_file_metric_tag: bool,
194    }
195
196    impl InternalEvent for FileEventsReceived<'_> {
197        fn emit(self) {
198            trace!(
199                message = "Events received.",
200                count = %self.count,
201                byte_size = %self.byte_size,
202                file = %self.file
203            );
204            if self.include_file_metric_tag {
205                counter!(
206                    CounterName::ComponentReceivedEventsTotal,
207                    "file" => self.file.to_owned(),
208                )
209                .increment(self.count as u64);
210                counter!(
211                    CounterName::ComponentReceivedEventBytesTotal,
212                    "file" => self.file.to_owned(),
213                )
214                .increment(self.byte_size.get() as u64);
215            } else {
216                counter!(CounterName::ComponentReceivedEventsTotal).increment(self.count as u64);
217                counter!(CounterName::ComponentReceivedEventBytesTotal)
218                    .increment(self.byte_size.get() as u64);
219            }
220        }
221    }
222
223    #[derive(Debug, NamedInternalEvent)]
224    pub struct FileChecksumFailed<'a> {
225        pub file: &'a Path,
226        pub include_file_metric_tag: bool,
227    }
228
229    impl InternalEvent for FileChecksumFailed<'_> {
230        fn emit(self) {
231            warn!(
232                message = "Currently ignoring file too small to fingerprint.",
233                file = %self.file.display(),
234            );
235            if self.include_file_metric_tag {
236                counter!(
237                    CounterName::ChecksumErrorsTotal,
238                    "file" => self.file.to_string_lossy().into_owned(),
239                )
240            } else {
241                counter!(CounterName::ChecksumErrorsTotal)
242            }
243            .increment(1);
244        }
245    }
246
247    #[derive(Debug, NamedInternalEvent)]
248    pub struct FileFingerprintReadError<'a> {
249        pub file: &'a Path,
250        pub error: Error,
251        pub include_file_metric_tag: bool,
252    }
253
254    impl InternalEvent for FileFingerprintReadError<'_> {
255        fn emit(self) {
256            error!(
257                message = "Failed reading file for fingerprinting.",
258                file = %self.file.display(),
259                error = %self.error,
260                error_code = "reading_fingerprint",
261                error_type = error_type::READER_FAILED,
262                stage = error_stage::RECEIVING,
263            );
264            if self.include_file_metric_tag {
265                counter!(
266                    CounterName::ComponentErrorsTotal,
267                    "error_code" => "reading_fingerprint",
268                    "error_type" => error_type::READER_FAILED,
269                    "stage" => error_stage::RECEIVING,
270                    "file" => self.file.to_string_lossy().into_owned(),
271                )
272            } else {
273                counter!(
274                    CounterName::ComponentErrorsTotal,
275                    "error_code" => "reading_fingerprint",
276                    "error_type" => error_type::READER_FAILED,
277                    "stage" => error_stage::RECEIVING,
278                )
279            }
280            .increment(1);
281        }
282    }
283
284    const DELETION_FAILED: &str = "deletion_failed";
285
286    #[derive(Debug, NamedInternalEvent)]
287    pub struct FileDeleteError<'a> {
288        pub file: &'a Path,
289        pub error: Error,
290        pub include_file_metric_tag: bool,
291    }
292
293    impl InternalEvent for FileDeleteError<'_> {
294        fn emit(self) {
295            error!(
296                message = "Failed in deleting file.",
297                file = %self.file.display(),
298                error = %self.error,
299                error_code = DELETION_FAILED,
300                error_type = error_type::COMMAND_FAILED,
301                stage = error_stage::RECEIVING,
302            );
303            if self.include_file_metric_tag {
304                counter!(
305                    CounterName::ComponentErrorsTotal,
306                    "file" => self.file.to_string_lossy().into_owned(),
307                    "error_code" => DELETION_FAILED,
308                    "error_type" => error_type::COMMAND_FAILED,
309                    "stage" => error_stage::RECEIVING,
310                )
311            } else {
312                counter!(
313                    CounterName::ComponentErrorsTotal,
314                    "error_code" => DELETION_FAILED,
315                    "error_type" => error_type::COMMAND_FAILED,
316                    "stage" => error_stage::RECEIVING,
317                )
318            }
319            .increment(1);
320        }
321    }
322
323    #[derive(Debug, NamedInternalEvent)]
324    pub struct FileDeleted<'a> {
325        pub file: &'a Path,
326        pub include_file_metric_tag: bool,
327    }
328
329    impl InternalEvent for FileDeleted<'_> {
330        fn emit(self) {
331            info!(
332                message = "File deleted.",
333                file = %self.file.display(),
334            );
335            if self.include_file_metric_tag {
336                counter!(
337                    CounterName::FilesDeletedTotal,
338                    "file" => self.file.to_string_lossy().into_owned(),
339                )
340            } else {
341                counter!(CounterName::FilesDeletedTotal)
342            }
343            .increment(1);
344        }
345    }
346
347    #[derive(Debug, NamedInternalEvent)]
348    pub struct FileUnwatched<'a> {
349        pub file: &'a Path,
350        pub include_file_metric_tag: bool,
351        pub reached_eof: bool,
352    }
353
354    impl InternalEvent for FileUnwatched<'_> {
355        fn emit(self) {
356            let reached_eof = if self.reached_eof { "true" } else { "false" };
357            info!(
358                message = "Stopped watching file.",
359                file = %self.file.display(),
360                reached_eof
361            );
362            if self.include_file_metric_tag {
363                counter!(
364                    CounterName::FilesUnwatchedTotal,
365                    "file" => self.file.to_string_lossy().into_owned(),
366                    "reached_eof" => reached_eof,
367                )
368            } else {
369                counter!(
370                    CounterName::FilesUnwatchedTotal,
371                    "reached_eof" => reached_eof,
372                )
373            }
374            .increment(1);
375        }
376    }
377
378    #[derive(Debug, NamedInternalEvent)]
379    struct FileWatchError<'a> {
380        pub file: &'a Path,
381        pub error: Error,
382        pub include_file_metric_tag: bool,
383    }
384
385    impl InternalEvent for FileWatchError<'_> {
386        fn emit(self) {
387            error!(
388                message = "Failed to watch file.",
389                error = %self.error,
390                error_code = "watching",
391                error_type = error_type::COMMAND_FAILED,
392                stage = error_stage::RECEIVING,
393                file = %self.file.display(),
394            );
395            if self.include_file_metric_tag {
396                counter!(
397                    CounterName::ComponentErrorsTotal,
398                    "error_code" => "watching",
399                    "error_type" => error_type::COMMAND_FAILED,
400                    "stage" => error_stage::RECEIVING,
401                    "file" => self.file.to_string_lossy().into_owned(),
402                )
403            } else {
404                counter!(
405                    CounterName::ComponentErrorsTotal,
406                    "error_code" => "watching",
407                    "error_type" => error_type::COMMAND_FAILED,
408                    "stage" => error_stage::RECEIVING,
409                )
410            }
411            .increment(1);
412        }
413    }
414
415    #[derive(Debug, NamedInternalEvent)]
416    pub struct FileResumed<'a> {
417        pub file: &'a Path,
418        pub file_position: u64,
419        pub include_file_metric_tag: bool,
420    }
421
422    impl InternalEvent for FileResumed<'_> {
423        fn emit(self) {
424            info!(
425                message = "Resuming to watch file.",
426                file = %self.file.display(),
427                file_position = %self.file_position
428            );
429            if self.include_file_metric_tag {
430                counter!(
431                    CounterName::FilesResumedTotal,
432                    "file" => self.file.to_string_lossy().into_owned(),
433                )
434            } else {
435                counter!(CounterName::FilesResumedTotal)
436            }
437            .increment(1);
438        }
439    }
440
441    #[derive(Debug, NamedInternalEvent)]
442    pub struct FileAdded<'a> {
443        pub file: &'a Path,
444        pub include_file_metric_tag: bool,
445    }
446
447    impl InternalEvent for FileAdded<'_> {
448        fn emit(self) {
449            info!(
450                message = "Found new file to watch.",
451                file = %self.file.display(),
452            );
453            if self.include_file_metric_tag {
454                counter!(
455                    CounterName::FilesAddedTotal,
456                    "file" => self.file.to_string_lossy().into_owned(),
457                )
458            } else {
459                counter!(CounterName::FilesAddedTotal)
460            }
461            .increment(1);
462        }
463    }
464
465    #[derive(Debug, NamedInternalEvent)]
466    pub struct FileCheckpointed {
467        pub count: usize,
468        pub duration: Duration,
469    }
470
471    impl InternalEvent for FileCheckpointed {
472        fn emit(self) {
473            debug!(
474                message = "Files checkpointed.",
475                count = %self.count,
476                duration_ms = self.duration.as_millis() as u64,
477            );
478            counter!(CounterName::CheckpointsTotal).increment(self.count as u64);
479        }
480    }
481
482    #[derive(Debug, NamedInternalEvent)]
483    pub struct FileCheckpointWriteError {
484        pub error: Error,
485    }
486
487    impl InternalEvent for FileCheckpointWriteError {
488        fn emit(self) {
489            error!(
490                message = "Failed writing checkpoints.",
491                error = %self.error,
492                error_code = "writing_checkpoints",
493                error_type = error_type::WRITER_FAILED,
494                stage = error_stage::RECEIVING,
495            );
496            counter!(
497                CounterName::ComponentErrorsTotal,
498                "error_code" => "writing_checkpoints",
499                "error_type" => error_type::WRITER_FAILED,
500                "stage" => error_stage::RECEIVING,
501            )
502            .increment(1);
503        }
504    }
505
506    #[derive(Debug, NamedInternalEvent)]
507    pub struct PathGlobbingError<'a> {
508        pub path: &'a Path,
509        pub error: &'a Error,
510    }
511
512    impl InternalEvent for PathGlobbingError<'_> {
513        fn emit(self) {
514            error!(
515                message = "Failed to glob path.",
516                error = %self.error,
517                error_code = "globbing",
518                error_type = error_type::READER_FAILED,
519                stage = error_stage::RECEIVING,
520                path = %self.path.display(),
521            );
522            counter!(
523                CounterName::ComponentErrorsTotal,
524                "error_code" => "globbing",
525                "error_type" => error_type::READER_FAILED,
526                "stage" => error_stage::RECEIVING,
527            )
528            .increment(1);
529        }
530    }
531
532    #[derive(Debug, NamedInternalEvent)]
533    pub struct FileLineTooBigError<'a> {
534        pub truncated_bytes: &'a BytesMut,
535        pub configured_limit: usize,
536        pub encountered_size_so_far: usize,
537    }
538
539    impl InternalEvent for FileLineTooBigError<'_> {
540        fn emit(self) {
541            error!(
542                message = "Found line that exceeds max_line_bytes; discarding.",
543                truncated_bytes = ?self.truncated_bytes,
544                configured_limit = self.configured_limit,
545                encountered_size_so_far = self.encountered_size_so_far,
546                error_type = error_type::CONDITION_FAILED,
547                stage = error_stage::RECEIVING,
548            );
549            counter!(
550                CounterName::ComponentErrorsTotal,
551                "error_code" => "reading_line_from_file",
552                "error_type" => error_type::CONDITION_FAILED,
553                "stage" => error_stage::RECEIVING,
554            )
555            .increment(1);
556            emit!(ComponentEventsDropped::<INTENTIONAL> {
557                count: 1,
558                reason: "Found line that exceeds max_line_bytes; discarding.",
559            });
560        }
561    }
562
563    #[derive(Clone)]
564    pub struct FileSourceInternalEventsEmitter {
565        pub include_file_metric_tag: bool,
566    }
567
568    impl FileSourceInternalEvents for FileSourceInternalEventsEmitter {
569        fn emit_file_added(&self, file: &Path) {
570            emit!(FileAdded {
571                file,
572                include_file_metric_tag: self.include_file_metric_tag
573            });
574        }
575
576        fn emit_file_resumed(&self, file: &Path, file_position: u64) {
577            emit!(FileResumed {
578                file,
579                file_position,
580                include_file_metric_tag: self.include_file_metric_tag
581            });
582        }
583
584        fn emit_file_watch_error(&self, file: &Path, error: Error) {
585            emit!(FileWatchError {
586                file,
587                error,
588                include_file_metric_tag: self.include_file_metric_tag
589            });
590        }
591
592        fn emit_file_unwatched(&self, file: &Path, reached_eof: bool) {
593            emit!(FileUnwatched {
594                file,
595                include_file_metric_tag: self.include_file_metric_tag,
596                reached_eof
597            });
598        }
599
600        fn emit_file_deleted(&self, file: &Path) {
601            emit!(FileDeleted {
602                file,
603                include_file_metric_tag: self.include_file_metric_tag
604            });
605        }
606
607        fn emit_file_delete_error(&self, file: &Path, error: Error) {
608            emit!(FileDeleteError {
609                file,
610                error,
611                include_file_metric_tag: self.include_file_metric_tag
612            });
613        }
614
615        fn emit_file_fingerprint_read_error(&self, file: &Path, error: Error) {
616            emit!(FileFingerprintReadError {
617                file,
618                error,
619                include_file_metric_tag: self.include_file_metric_tag
620            });
621        }
622
623        fn emit_file_checksum_failed(&self, file: &Path) {
624            emit!(FileChecksumFailed {
625                file,
626                include_file_metric_tag: self.include_file_metric_tag
627            });
628        }
629
630        fn emit_file_checkpointed(&self, count: usize, duration: Duration) {
631            emit!(FileCheckpointed { count, duration });
632        }
633
634        fn emit_file_checkpoint_write_error(&self, error: Error) {
635            emit!(FileCheckpointWriteError { error });
636        }
637
638        fn emit_files_open(&self, count: usize) {
639            emit!(FileOpen { count });
640        }
641
642        fn emit_path_globbing_failed(&self, path: &Path, error: &Error) {
643            emit!(PathGlobbingError { path, error });
644        }
645
646        fn emit_file_line_too_long(
647            &self,
648            truncated_bytes: &bytes::BytesMut,
649            configured_limit: usize,
650            encountered_size_so_far: usize,
651        ) {
652            emit!(FileLineTooBigError {
653                truncated_bytes,
654                configured_limit,
655                encountered_size_so_far
656            });
657        }
658    }
659}