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