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] | [`LevelFilter::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 /// [`FullFormatter`]: crate::formatter::FullFormatter
51 /// [error_handler]: FileSinkBuilder::error_handler
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**, and defaults to `false`.
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**, and defaults to the value consistent
168 /// with `std`.
169 #[must_use]
170 pub fn capacity(mut self, capacity: usize) -> Self {
171 self.capacity = Some(capacity);
172 self
173 }
174
175 // Prop
176 //
177
178 /// Specifies a log level filter.
179 ///
180 /// This parameter is **optional**, and defaults to [`LevelFilter::All`].
181 #[must_use]
182 pub fn level_filter(self, level_filter: LevelFilter) -> Self {
183 self.prop.set_level_filter(level_filter);
184 self
185 }
186
187 /// Specifies a formatter.
188 ///
189 /// This parameter is **optional**, and defaults to [`FullFormatter`].
190 ///
191 /// [`FullFormatter`]: crate::formatter::FullFormatter
192 #[must_use]
193 pub fn formatter<F>(self, formatter: F) -> Self
194 where
195 F: Formatter + 'static,
196 {
197 self.prop.set_formatter(formatter);
198 self
199 }
200
201 /// Specifies an error handler.
202 ///
203 /// This parameter is **optional**, and defaults to
204 /// [`ErrorHandler::default()`].
205 #[must_use]
206 pub fn error_handler<F: Into<ErrorHandler>>(self, handler: F) -> Self {
207 self.prop.set_error_handler(handler);
208 self
209 }
210}
211
212impl FileSinkBuilder<()> {
213 #[doc(hidden)]
214 #[deprecated(note = "\n\n\
215 builder compile-time error:\n\
216 - missing required parameter `path`\n\n\
217 ")]
218 pub fn build(self, _: Infallible) {}
219
220 #[doc(hidden)]
221 #[deprecated(note = "\n\n\
222 builder compile-time error:\n\
223 - missing required parameter `path`\n\n\
224 ")]
225 pub fn build_arc(self, _: Infallible) {}
226}
227
228impl FileSinkBuilder<PathBuf> {
229 /// Builds a [`FileSink`].
230 ///
231 /// # Error
232 ///
233 /// If an error occurs opening the file, [`Error::CreateDirectory`] or
234 /// [`Error::OpenFile`] will be returned.
235 pub fn build(self) -> Result<FileSink> {
236 let file = utils::open_file_bufw(self.path, self.truncate, self.capacity)?;
237
238 let sink = FileSink {
239 prop: self.prop,
240 file: Mutex::new(file),
241 };
242
243 Ok(sink)
244 }
245
246 /// Builds a `Arc<FileSink>`.
247 ///
248 /// This is a shorthand method for `.build().map(Arc::new)`.
249 pub fn build_arc(self) -> Result<Arc<FileSink>> {
250 self.build().map(Arc::new)
251 }
252}