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::FormatterContext,
12 sink::{helper, Sink},
13 sync::*,
14 utils, Error, 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 common_impl: helper::CommonImpl,
32 file: SpinMutex<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] | [default error handler] |
43 /// | | |
44 /// | [path] | *must be specified* |
45 /// | [truncate] | `false` |
46 ///
47 /// [level_filter]: FileSinkBuilder::level_filter
48 /// [formatter]: FileSinkBuilder::formatter
49 /// [error_handler]: FileSinkBuilder::error_handler
50 /// [default error handler]: error/index.html#default-error-handler
51 /// [path]: FileSinkBuilder::path
52 /// [truncate]: FileSinkBuilder::truncate
53 #[must_use]
54 pub fn builder() -> FileSinkBuilder<()> {
55 FileSinkBuilder {
56 path: (),
57 truncate: false,
58 common_builder_impl: helper::CommonBuilderImpl::new(),
59 }
60 }
61
62 /// Constructs a `FileSink`.
63 ///
64 /// If the parameter `truncate` is `true`, the existing contents of the file
65 /// will be discarded.
66 ///
67 /// # Error
68 ///
69 /// If an error occurs opening the file, [`Error::CreateDirectory`] or
70 /// [`Error::OpenFile`] will be returned.
71 #[deprecated(
72 since = "0.3.0",
73 note = "it may be removed in the future, use `FileSink::builder()` instead"
74 )]
75 pub fn new<P>(path: P, truncate: bool) -> Result<FileSink>
76 where
77 P: AsRef<Path>, /* Keep the `AsRef<Path>` instead of `Into<PathBuf>` for backward
78 * compatible */
79 {
80 Self::builder()
81 .path(path.as_ref())
82 .truncate(truncate)
83 .build()
84 }
85}
86
87impl Sink for FileSink {
88 fn log(&self, record: &Record) -> Result<()> {
89 let mut string_buf = StringBuf::new();
90 let mut ctx = FormatterContext::new();
91 self.common_impl
92 .formatter
93 .read()
94 .format(record, &mut string_buf, &mut ctx)?;
95
96 self.file
97 .lock()
98 .write_all(string_buf.as_bytes())
99 .map_err(Error::WriteRecord)?;
100
101 Ok(())
102 }
103
104 fn flush(&self) -> Result<()> {
105 self.file.lock().flush().map_err(Error::FlushBuffer)
106 }
107
108 helper::common_impl!(@Sink: common_impl);
109}
110
111impl Drop for FileSink {
112 fn drop(&mut self) {
113 if let Err(err) = self.file.lock().flush() {
114 self.common_impl
115 .non_returnable_error("FileSink", Error::FlushBuffer(err))
116 }
117 }
118}
119
120// --------------------------------------------------
121
122/// #
123#[doc = include_str!("../include/doc/generic-builder-note.md")]
124pub struct FileSinkBuilder<ArgPath> {
125 common_builder_impl: helper::CommonBuilderImpl,
126 path: ArgPath,
127 truncate: bool,
128}
129
130impl<ArgPath> FileSinkBuilder<ArgPath> {
131 /// The path of the log file.
132 ///
133 /// This parameter is **required**.
134 #[must_use]
135 pub fn path<P>(self, path: P) -> FileSinkBuilder<PathBuf>
136 where
137 P: Into<PathBuf>,
138 {
139 FileSinkBuilder {
140 common_builder_impl: self.common_builder_impl,
141 path: path.into(),
142 truncate: self.truncate,
143 }
144 }
145
146 /// Truncates the contents when opening an existing file.
147 ///
148 /// If it is `true`, the existing contents of the file will be discarded.
149 ///
150 /// This parameter is **optional**.
151 #[must_use]
152 pub fn truncate(mut self, truncate: bool) -> Self {
153 self.truncate = truncate;
154 self
155 }
156
157 helper::common_impl!(@SinkBuilder: common_builder_impl);
158}
159
160impl FileSinkBuilder<()> {
161 #[doc(hidden)]
162 #[deprecated(note = "\n\n\
163 builder compile-time error:\n\
164 - missing required parameter `path`\n\n\
165 ")]
166 pub fn build(self, _: Infallible) {}
167}
168
169impl FileSinkBuilder<PathBuf> {
170 /// Builds a [`FileSink`].
171 ///
172 /// # Error
173 ///
174 /// If an error occurs opening the file, [`Error::CreateDirectory`] or
175 /// [`Error::OpenFile`] will be returned.
176 pub fn build(self) -> Result<FileSink> {
177 let file = utils::open_file(self.path, self.truncate)?;
178
179 let sink = FileSink {
180 common_impl: helper::CommonImpl::from_builder(self.common_builder_impl),
181 file: SpinMutex::new(BufWriter::new(file)),
182 };
183
184 Ok(sink)
185 }
186}