Skip to main content

journal_log_writer/log/
config.rs

1use journal_core::file::{
2    Compression, DEFAULT_COMPRESS_THRESHOLD, DEFAULT_JOURNAL_FILE_MODE, FieldNamePolicy,
3    normalize_compress_threshold,
4};
5use journal_registry::Origin;
6use std::time::Duration;
7use uuid::Uuid;
8
9/// Controls whether [`Log`](crate::Log) creates or opens the active file at
10/// construction time or waits for the first append.
11#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
12pub enum LogOpenMode {
13    /// Validate and scan the directory during construction, then create the
14    /// active journal file on the first append.
15    #[default]
16    Lazy,
17    /// Create or open the active journal file during construction.
18    Eager,
19}
20
21/// Controls how missing machine and boot identities are handled.
22#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
23pub enum LogIdentityMode {
24    /// Legacy mode retained only so older callers receive a clear error.
25    ///
26    /// The writer no longer generates SDK-local machine or boot IDs. Callers
27    /// must provide explicit IDs or call the optional host helper and pass the
28    /// returned values into the config.
29    #[deprecated(
30        since = "0.7.2",
31        note = "use LogIdentityMode::Strict and supply explicit IDs"
32    )]
33    Auto,
34    /// Require explicit machine ID and boot ID in the config.
35    #[default]
36    Strict,
37}
38
39/// Controls when journal files should be rotated
40///
41/// A file rotates when *any* configured limit is exceeded. If all fields are `None`,
42/// files never rotate automatically.
43#[derive(Debug, Copy, Clone, Default)]
44pub struct RotationPolicy {
45    /// Maximum file size
46    pub size_of_journal_file: Option<u64>,
47    /// Maximum duration of head/tail entries
48    pub duration_of_journal_file: Option<Duration>,
49    /// Maximum number of log entries
50    pub number_of_entries: Option<usize>,
51}
52
53impl RotationPolicy {
54    /// Specifies the maximum journal file size.
55    pub fn with_size_of_journal_file(mut self, size_of_journal_file: u64) -> Self {
56        self.size_of_journal_file = Some(size_of_journal_file);
57        self
58    }
59
60    /// Specifies the maximum duration between head/tail entry.
61    pub fn with_duration_of_journal_file(mut self, duration_of_journal_file: Duration) -> Self {
62        self.duration_of_journal_file = Some(duration_of_journal_file);
63        self
64    }
65
66    /// Specifies maximum number of entries.
67    pub fn with_number_of_entries(mut self, number_of_entries: usize) -> Self {
68        self.number_of_entries = Some(number_of_entries);
69        self
70    }
71}
72
73/// Controls when old journal files should be deleted.
74///
75/// Old files are removed to satisfy *all* configured limits. Removal starts with
76/// the oldest files first. If all fields are `None`, files are never deleted.
77#[derive(Debug, Copy, Clone, Default)]
78pub struct RetentionPolicy {
79    /// Maximum number of journal files to keep
80    pub number_of_journal_files: Option<usize>,
81    /// Maximum total size of all journal files (in bytes)
82    pub size_of_journal_files: Option<u64>,
83    /// Maximum age of files to keep
84    pub duration_of_journal_files: Option<Duration>,
85}
86
87impl RetentionPolicy {
88    /// Specifies maximum number of journal files.
89    pub fn with_number_of_journal_files(mut self, number_of_journal_files: usize) -> Self {
90        self.number_of_journal_files = Some(number_of_journal_files);
91        self
92    }
93
94    /// Specifies maximum size of journal files.
95    pub fn with_size_of_journal_files(mut self, size_of_journal_files: u64) -> Self {
96        self.size_of_journal_files = Some(size_of_journal_files);
97        self
98    }
99
100    /// Specifies maximum duration of journal files.
101    pub fn with_duration_of_journal_files(mut self, duration_of_journal_files: Duration) -> Self {
102        self.duration_of_journal_files = Some(duration_of_journal_files);
103        self
104    }
105}
106
107/// Configuration for a journal log.
108#[derive(Debug, Clone)]
109pub struct Config {
110    pub origin: Origin,
111    /// Policy for when to rotate active files
112    pub rotation_policy: RotationPolicy,
113    /// Policy for when to remove old files
114    pub retention_policy: RetentionPolicy,
115    /// DATA object compression algorithm.
116    pub compression: Compression,
117    /// Minimum uncompressed DATA payload size before compression is attempted.
118    pub compression_threshold: usize,
119    /// Use the systemd compact journal on-disk layout.
120    pub compact: bool,
121    /// Active-file creation mode.
122    pub open_mode: LogOpenMode,
123    /// Missing identity handling mode.
124    pub identity_mode: LogIdentityMode,
125    /// Optional boot ID override for new files and strict identity mode.
126    pub boot_id: Option<Uuid>,
127    /// Use systemd's `<source>.journal` active filename policy.
128    ///
129    /// The default is false so active files use the chain filename form
130    /// `<source>@<seqnum_id>-<head_seqnum>-<head_realtime>.journal`.
131    pub strict_systemd_naming: bool,
132    /// Explicit live-reader publication cadence.
133    ///
134    /// `1` is the default systemd-compatible behavior, `0` disables explicit
135    /// per-entry live publication, and values greater than `1` publish after
136    /// every N entries.
137    pub live_publish_every_entries: u64,
138    /// Field-name policy for caller-provided fields.
139    pub field_name_policy: FieldNamePolicy,
140    /// Permission bits for newly created journal files on Unix platforms.
141    ///
142    /// The default follows systemd journald's 0640 journal-file mode.
143    pub file_mode: u32,
144}
145
146impl Config {
147    /// Creates a new log configuration.
148    pub fn new(
149        origin: Origin,
150        rotation_policy: RotationPolicy,
151        retention_policy: RetentionPolicy,
152    ) -> Self {
153        Self {
154            origin,
155            rotation_policy,
156            retention_policy,
157            compression: Compression::None,
158            compression_threshold: DEFAULT_COMPRESS_THRESHOLD,
159            compact: false,
160            open_mode: LogOpenMode::Lazy,
161            identity_mode: LogIdentityMode::Strict,
162            boot_id: None,
163            strict_systemd_naming: false,
164            live_publish_every_entries: 1,
165            field_name_policy: FieldNamePolicy::Journald,
166            file_mode: DEFAULT_JOURNAL_FILE_MODE,
167        }
168    }
169
170    /// Specifies the rotation policy of the log directory
171    pub fn with_rotation_policy(mut self, policy: RotationPolicy) -> Self {
172        self.rotation_policy = policy;
173        self
174    }
175
176    /// Specifies the retention policy of the log directory
177    pub fn with_retention_policy(mut self, policy: RetentionPolicy) -> Self {
178        self.retention_policy = policy;
179        self
180    }
181
182    pub fn with_compression(mut self, compression: Compression) -> Self {
183        self.compression = compression;
184        self
185    }
186
187    pub fn with_compression_threshold(mut self, threshold: usize) -> Self {
188        self.compression_threshold = normalize_compress_threshold(threshold);
189        self
190    }
191
192    pub fn with_compact(mut self, compact: bool) -> Self {
193        self.compact = compact;
194        self
195    }
196
197    pub fn with_open_mode(mut self, open_mode: LogOpenMode) -> Self {
198        self.open_mode = open_mode;
199        self
200    }
201
202    pub fn with_identity_mode(mut self, identity_mode: LogIdentityMode) -> Self {
203        self.identity_mode = identity_mode;
204        self
205    }
206
207    pub fn with_boot_id(mut self, boot_id: Uuid) -> Self {
208        self.boot_id = Some(boot_id);
209        self
210    }
211
212    pub fn with_strict_systemd_naming(mut self, strict_systemd_naming: bool) -> Self {
213        self.strict_systemd_naming = strict_systemd_naming;
214        self
215    }
216
217    pub fn with_live_publish_every_entries(mut self, entries: u64) -> Self {
218        self.live_publish_every_entries = entries;
219        self
220    }
221
222    pub fn with_field_name_policy(mut self, policy: FieldNamePolicy) -> Self {
223        self.field_name_policy = policy;
224        self
225    }
226
227    pub fn with_file_mode(mut self, mode: u32) -> Self {
228        assert!(
229            mode <= 0o777,
230            "journal file mode must contain only permission bits"
231        );
232        self.file_mode = mode;
233        self
234    }
235}