1 #![deny(missing_docs)]
2 #![doc(html_root_url = "https://docs.rs/fern/0.6.0")]
3 //! Efficient, configurable logging in Rust.
4 //!
5 //! # Depending on fern
6 //!
7 //! Ensure you require both fern and log in your project's `Cargo.toml`:
8 //!
9 //! ```toml
10 //! [dependencies]
11 //! log = "0.4"
12 //! fern = "0.5"
13 //! ```
14 //!
15 //! # Example setup
16 //!
17 //! With fern, all logger configuration is done via builder-like methods on
18 //! instances of the [`Dispatch`] structure.
19 //!
20 //! Here's an example logger which formats messages, and sends everything Debug
21 //! and above to both stdout and an output.log file:
22 //!
23 //! ```no_run
24 //! use log::{debug, error, info, trace, warn};
25 //!
26 //! fn setup_logger() -> Result<(), fern::InitError> {
27 //!     fern::Dispatch::new()
28 //!         .format(|out, message, record| {
29 //!             out.finish(format_args!(
30 //!                 "{}[{}][{}] {}",
31 //!                 chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
32 //!                 record.target(),
33 //!                 record.level(),
34 //!                 message
35 //!             ))
36 //!         })
37 //!         .level(log::LevelFilter::Debug)
38 //!         .chain(std::io::stdout())
39 //!         .chain(fern::log_file("output.log")?)
40 //!         .apply()?;
41 //!     Ok(())
42 //! }
43 //! # fn main() {
44 //! #     setup_logger().expect("failed to set up logger")
45 //! # }
46 //! ```
47 //!
48 //! Let's unwrap this:
49 //!
50 //! ---
51 //!
52 //! [`fern::Dispatch::new()`]
53 //!
54 //! Create an empty configuration.
55 //!
56 //! ---
57 //!
58 //! [`.format(|...| ...)`]
59 //!
60 //! Add a formatter to the logger, modifying all messages sent through.
61 //!
62 //! ___
63 //!
64 //! [`chrono::Local::now()`]
65 //!
66 //! Get the current time in the local timezone using the [`chrono`] library.
67 //! See the [time-and-date docs].
68 //!
69 //! ___
70 //!
71 //! [`.format("[%Y-%m-%d][%H:%M:%S]")`][chrono-format]
72 //!
73 //! Use chrono's lazy format specifier to turn the time into a readable string.
74 //!
75 //! ---
76 //!
77 //! [`out.finish(format_args!(...))`]
78 //!
79 //! Call the `fern::FormattingCallback` to submit the formatted message.
80 //!
81 //! This roundabout way is slightly odd, but it allows for very fast logging.
82 //! No string allocation required!
83 //!
84 //! [`format_args!()`] has the same format as [`println!()`] \(and every other
85 //! [`std::fmt`]-based macro).
86 //!
87 //! ---
88 //!
89 //! [`.level(log::LevelFilter::Debug)`]
90 //!
91 //! Set the minimum level needed to output to `Debug`.
92 //!
93 //! ---
94 //!
95 //! [`.chain(std::io::stdout())`]
96 //!
97 //! Add a child to the logger. All messages which pass the filters will be sent
98 //! to stdout.
99 //!
100 //! [`Dispatch::chain`] accepts [`Stdout`], [`Stderr`], [`File`] and other
101 //! [`Dispatch`] instances.
102 //!
103 //! ---
104 //!
105 //! [`.chain(fern::log_file(...)?)`]
106 //!
107 //! Add a second child sending messages to the file "output.log".
108 //!
109 //! See [`fern::log_file()`] for more info on file output.
110 //!
111 //! ---
112 //!
113 //! [`.apply()`][`.apply`]
114 //!
115 //! Consume the configuration and instantiate it as the current runtime global
116 //! logger.
117 //!
118 //! This will fail if and only if `.apply()` or equivalent form another crate
119 //! has already been used this runtime.
120 //!
121 //! Since the binary crate is the only one ever setting up logging, the
122 //! [`apply`] result can be reasonably unwrapped: it's a bug if any crate is
123 //! calling this method more than once.
124 //!
125 //! ---
126 //!
127 //! The final output will look like:
128 //!
129 //! ```text
130 //! [2017-01-20][12:55:04][crate-name][INFO] Hello, world!
131 //! [2017-01-20][12:56:21][crate-name][WARN] Ahhh!
132 //! [2017-01-20][12:58:00][crate-name][DEBUG] Something less important happened.
133 //! ```
134 //!
135 //! # Logging
136 //!
137 //! Once the logger has been set, it will pick up all logging calls from your
138 //! crate and all libraries you depend on.
139 //!
140 //! ```rust
141 //! # use log::{debug, error, info, trace, warn};
142 //!
143 //! # fn setup_logger() -> Result<(), fern::InitError> {
144 //! fern::Dispatch::new()
145 //!     // ...
146 //!     .apply()?;
147 //! # Ok(())
148 //! # }
149 //!
150 //! # fn main() {
151 //! # setup_logger().ok(); // we're ok with this not succeeding.
152 //! trace!("Trace message");
153 //! debug!("Debug message");
154 //! info!("Info message");
155 //! warn!("Warning message");
156 //! error!("Error message");
157 //! # }
158 //! ```
159 //!
160 //! # More
161 //!
162 //! The [`Dispatch` documentation] has example usages of each method, and the
163 //! [full example program] might be useful for using fern in a larger
164 //! application context.
165 //!
166 //! See the [colors] module for examples using ANSI terminal coloring.
167 //!
168 //! See the [syslog] module for examples outputting to the unix syslog, or the
169 //! [syslog full example program] for a more realistic sample.
170 //!
171 //! See the [meta] module for information on getting logging-within-logging
172 //! working correctly.
173 //!
174 //! [`fern::Dispatch::new()`]: struct.Dispatch.html#method.new
175 //! [`.format(|...| ...)`]: struct.Dispatch.html#method.format
176 //! [`chrono::Local::now()`]: https://docs.rs/chrono/0.4/chrono/offset/local/struct.Local.html#method.now
177 //! [chrono-format]: https://docs.rs/chrono/0.4/chrono/datetime/struct.DateTime.html#method.format
178 //! [`out.finish(format_args!(...))`]: struct.FormatCallback.html#method.finish
179 //! [`.level(log::LevelFilter::Debug)`]: struct.Dispatch.html#method.level
180 //! [`Dispatch::chain`]: struct.Dispatch.html#method.chain
181 //! [`.chain(std::io::stdout())`]: struct.Dispatch.html#method.chain
182 //! [`Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html
183 //! [`Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html
184 //! [`File`]: https://doc.rust-lang.org/std/fs/struct.File.html
185 //! [`Dispatch`]: struct.Dispatch.html
186 //! [`.chain(fern::log_file(...)?)`]: struct.Dispatch.html#method.chain
187 //! [`fern::log_file()`]: fn.log_file.html
188 //! [`.apply`]: struct.Dispatch.html#method.apply
189 //! [`format_args!()`]: https://doc.rust-lang.org/std/macro.format_args.html
190 //! [`println!()`]: https://doc.rust-lang.org/std/macro.println.html
191 //! [`std::fmt`]: https://doc.rust-lang.org/std/fmt/
192 //! [`chrono`]: https://github.com/chronotope/chrono
193 //! [time-and-date docs]: https://docs.rs/chrono/0.4/chrono/index.html#date-and-time
194 //! [the format specifier docs]: https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers
195 //! [`Dispatch` documentation]: struct.Dispatch.html
196 //! [full example program]: https://github.com/daboross/fern/tree/master/examples/cmd-program.rs
197 //! [syslog full example program]: https://github.com/daboross/fern/tree/master/examples/syslog.rs
198 //! [`apply`]: struct.Dispatch.html#method.apply
199 //! [colors]: colors/index.html
200 //! [syslog]: syslog/index.html
201 //! [meta]: meta/index.html
202 use std::{
203     convert::AsRef,
204     fmt,
205     fs::{File, OpenOptions},
206     io,
207     path::Path,
208 };
209 
210 #[cfg(all(not(windows), feature = "syslog-4"))]
211 use std::collections::HashMap;
212 
213 pub use crate::{
214     builders::{Dispatch, Output, Panic},
215     errors::InitError,
216     log_impl::FormatCallback,
217 };
218 
219 mod builders;
220 mod errors;
221 mod log_impl;
222 
223 #[cfg(feature = "colored")]
224 pub mod colors;
225 #[cfg(all(not(windows), feature = "syslog-3", feature = "syslog-4"))]
226 pub mod syslog;
227 
228 pub mod meta;
229 
230 /// A type alias for a log formatter.
231 ///
232 /// As of fern `0.5`, the passed `fmt::Arguments` will always be the same as
233 /// the given `log::Record`'s `.args()`.
234 pub type Formatter = dyn Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static;
235 
236 /// A type alias for a log filter. Returning true means the record should
237 /// succeed - false means it should fail.
238 pub type Filter = dyn Fn(&log::Metadata) -> bool + Send + Sync + 'static;
239 
240 #[cfg(feature = "date-based")]
241 pub use crate::builders::DateBased;
242 
243 #[cfg(all(not(windows), feature = "syslog-4"))]
244 type Syslog4Rfc3164Logger = syslog4::Logger<syslog4::LoggerBackend, String, syslog4::Formatter3164>;
245 
246 #[cfg(all(not(windows), feature = "syslog-4"))]
247 type Syslog4Rfc5424Logger = syslog4::Logger<
248     syslog4::LoggerBackend,
249     (i32, HashMap<String, HashMap<String, String>>, String),
250     syslog4::Formatter5424,
251 >;
252 
253 /// Convenience method for opening a log file with common options.
254 ///
255 /// Equivalent to:
256 ///
257 /// ```no_run
258 /// std::fs::OpenOptions::new()
259 ///     .write(true)
260 ///     .create(true)
261 ///     .append(true)
262 ///     .open("filename")
263 /// # ;
264 /// ```
265 ///
266 /// See [`OpenOptions`] for more information.
267 ///
268 /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
269 #[inline]
log_file<P: AsRef<Path>>(path: P) -> io::Result<File>270 pub fn log_file<P: AsRef<Path>>(path: P) -> io::Result<File> {
271     OpenOptions::new()
272         .write(true)
273         .create(true)
274         .append(true)
275         .open(path)
276 }
277 
278 /// Convenience method for opening a re-openable log file with common options.
279 ///
280 /// The file opening is equivalent to:
281 ///
282 /// ```no_run
283 /// std::fs::OpenOptions::new()
284 ///     .write(true)
285 ///     .create(true)
286 ///     .append(true)
287 ///     .open("filename")
288 /// # ;
289 /// ```
290 ///
291 /// See [`OpenOptions`] for more information.
292 ///
293 /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
294 #[cfg(all(not(windows), feature = "reopen-03"))]
295 #[inline]
log_reopen(path: &Path, signal: Option<libc::c_int>) -> io::Result<reopen::Reopen<File>>296 pub fn log_reopen(path: &Path, signal: Option<libc::c_int>) -> io::Result<reopen::Reopen<File>> {
297     let p = path.to_owned();
298     let r = reopen::Reopen::new(Box::new(move || log_file(&p)))?;
299 
300     if let Some(s) = signal {
301         if let Err(e) = r.handle().register_signal(s) {
302             return Err(e);
303         }
304     }
305     Ok(r)
306 }
307