spdlog/sink/file_sink.rs
1//! Provides a file sink.
2
3use std::{
4 convert::Infallible,
5 fs::File,
6 io::{BufWriter, Write},
7 path::{Path, PathBuf},
8};
9
10use crate::{
11 formatter::{Formatter, FormatterContext},
12 sink::{GetSinkProp, Sink, SinkProp},
13 sync::*,
14 utils, Error, ErrorHandler, LevelFilter, Record, Result, StringBuf,
15};
16
17/// A sink with a file as the target.
18///
19/// It writes logs to a single file. If you want to automatically rotate into
20/// multiple files, see [`RotatingFileSink`].
21///
22/// The file and directories will be created recursively if they do not exist.
23///
24/// # Examples
25///
26/// See [./examples] directory.
27///
28/// [`RotatingFileSink`]: crate::sink::RotatingFileSink
29/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
30pub struct FileSink {
31 prop: SinkProp,
32 file: Mutex<BufWriter<File>>,
33}
34
35impl FileSink {
36 /// Gets a builder of `FileSink` with default parameters:
37 ///
38 /// | Parameter | Default Value |
39 /// |-----------------|-----------------------------|
40 /// | [level_filter] | `All` |
41 /// | [formatter] | `FullFormatter` |
42 /// | [error_handler] | [`ErrorHandler::default()`] |
43 /// | | |
44 /// | [path] | *must be specified* |
45 /// | [truncate] | `false` |
46 /// | [capacity] | consistent with `std` |
47 ///
48 /// [level_filter]: FileSinkBuilder::level_filter
49 /// [formatter]: FileSinkBuilder::formatter
50 /// [error_handler]: FileSinkBuilder::error_handler
51 /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
52 /// [path]: FileSinkBuilder::path
53 /// [truncate]: FileSinkBuilder::truncate
54 /// [capacity]: FileSinkBuilder::capacity
55 #[must_use]
56 pub fn builder() -> FileSinkBuilder<()> {
57 FileSinkBuilder {
58 prop: SinkProp::default(),
59 path: (),
60 truncate: false,
61 capacity: None,
62 }
63 }
64
65 /// Constructs a `FileSink`.
66 ///
67 /// If the parameter `truncate` is `true`, the existing contents of the file
68 /// will be discarded.
69 ///
70 /// # Error
71 ///
72 /// If an error occurs opening the file, [`Error::CreateDirectory`] or
73 /// [`Error::OpenFile`] will be returned.
74 #[deprecated(
75 since = "0.3.0",
76 note = "it may be removed in the future, use `FileSink::builder()` instead"
77 )]
78 pub fn new<P>(path: P, truncate: bool) -> Result<FileSink>
79 where
80 P: AsRef<Path>, /* Keep the `AsRef<Path>` instead of `Into<PathBuf>` for backward
81 * compatible */
82 {
83 Self::builder()
84 .path(path.as_ref())
85 .truncate(truncate)
86 .build()
87 }
88}
89
90impl GetSinkProp for FileSink {
91 fn prop(&self) -> &SinkProp {
92 &self.prop
93 }
94}
95
96impl Sink for FileSink {
97 fn log(&self, record: &Record) -> Result<()> {
98 let mut string_buf = StringBuf::new();
99 let mut ctx = FormatterContext::new();
100 self.prop
101 .formatter()
102 .format(record, &mut string_buf, &mut ctx)?;
103
104 self.file
105 .lock_expect()
106 .write_all(string_buf.as_bytes())
107 .map_err(Error::WriteRecord)?;
108
109 Ok(())
110 }
111
112 fn flush(&self) -> Result<()> {
113 self.file.lock_expect().flush().map_err(Error::FlushBuffer)
114 }
115}
116
117impl Drop for FileSink {
118 fn drop(&mut self) {
119 if let Err(err) = self.file.lock_expect().flush() {
120 self.prop
121 .call_error_handler_internal("FileSink", Error::FlushBuffer(err))
122 }
123 }
124}
125
126// --------------------------------------------------
127
128/// #
129#[doc = include_str!("../include/doc/generic-builder-note.md")]
130pub struct FileSinkBuilder<ArgPath> {
131 prop: SinkProp,
132 path: ArgPath,
133 truncate: bool,
134 capacity: Option<usize>,
135}
136
137impl<ArgPath> FileSinkBuilder<ArgPath> {
138 /// The path of the log file.
139 ///
140 /// This parameter is **required**.
141 #[must_use]
142 pub fn path<P>(self, path: P) -> FileSinkBuilder<PathBuf>
143 where
144 P: Into<PathBuf>,
145 {
146 FileSinkBuilder {
147 prop: self.prop,
148 path: path.into(),
149 truncate: self.truncate,
150 capacity: self.capacity,
151 }
152 }
153
154 /// Truncates the contents when opening an existing file.
155 ///
156 /// If it is `true`, the existing contents of the file will be discarded.
157 ///
158 /// This parameter is **optional**.
159 #[must_use]
160 pub fn truncate(mut self, truncate: bool) -> Self {
161 self.truncate = truncate;
162 self
163 }
164
165 /// Specifies the internal buffer capacity.
166 ///
167 /// This parameter is **optional**.
168 #[must_use]
169 pub fn capacity(mut self, capacity: usize) -> Self {
170 self.capacity = Some(capacity);
171 self
172 }
173
174 // Prop
175 //
176
177 /// Specifies a log level filter.
178 ///
179 /// This parameter is **optional**.
180 #[must_use]
181 pub fn level_filter(self, level_filter: LevelFilter) -> Self {
182 self.prop.set_level_filter(level_filter);
183 self
184 }
185
186 /// Specifies a formatter.
187 ///
188 /// This parameter is **optional**.
189 #[must_use]
190 pub fn formatter<F>(self, formatter: F) -> Self
191 where
192 F: Formatter + 'static,
193 {
194 self.prop.set_formatter(formatter);
195 self
196 }
197
198 /// Specifies an error handler.
199 ///
200 /// This parameter is **optional**.
201 #[must_use]
202 pub fn error_handler<F: Into<ErrorHandler>>(self, handler: F) -> Self {
203 self.prop.set_error_handler(handler);
204 self
205 }
206}
207
208impl FileSinkBuilder<()> {
209 #[doc(hidden)]
210 #[deprecated(note = "\n\n\
211 builder compile-time error:\n\
212 - missing required parameter `path`\n\n\
213 ")]
214 pub fn build(self, _: Infallible) {}
215}
216
217impl FileSinkBuilder<PathBuf> {
218 /// Builds a [`FileSink`].
219 ///
220 /// # Error
221 ///
222 /// If an error occurs opening the file, [`Error::CreateDirectory`] or
223 /// [`Error::OpenFile`] will be returned.
224 pub fn build(self) -> Result<FileSink> {
225 let file = utils::open_file_bufw(self.path, self.truncate, self.capacity)?;
226
227 let sink = FileSink {
228 prop: self.prop,
229 file: Mutex::new(file),
230 };
231
232 Ok(sink)
233 }
234}