Switch from log to tracing

This switches to using the tracing crate instead of log. Tracing
provides a lot of nice features which we can take advantage of moving
forward.

This also adjusts the output fairly significantly. This includes:

- Switched the environment variable from RUST_LOG to MDBOOK_LOG.
- Dropped the timestamp. I experimented with various different time
  displays, but ultimately decided to omit it for now. I don't think
  I've ever found it to be useful, and it takes up a very significant
  amount of space. It could potentially be useful for basic profiling,
  but I think there are other, better mechanisms for that. We could
  consider leveraging tracing itself for doing some basic profiling
  (like using something like tracing-chrome).
- Dropped the target unless MDBOOK_LOG is set. The target tends to be
  pretty noisy, and doesn't really convey much information unless you
  are debugging or otherwise trying to adjust the log output.
- Added color.
- Slightly reworked the way the error cause trace is displayed.
- Slightly changed the way html5ever filtering is done, as well as add
  handlebars to the list since they both are very noisy. You can
  override this now by explicitly listing them as targets.

I still expect that mdbook will eventually change how it displays things
to the console, possibly switching away from tracing and printing things
itself. However, that is a larger project for the future.
This commit is contained in:
Eric Huss 2025-09-12 06:13:45 -07:00
parent 787882069e
commit 3e673ce424
46 changed files with 320 additions and 292 deletions

166
Cargo.lock generated
View file

@ -559,29 +559,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "env_filter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"jiff",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@ -1132,30 +1109,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
dependencies = [
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde",
]
[[package]]
name = "jiff-static"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "js-sys"
version = "0.3.77"
@ -1186,6 +1139,12 @@ dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.174"
@ -1291,6 +1250,15 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "matchers"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata",
]
[[package]]
name = "matchit"
version = "0.8.4"
@ -1306,11 +1274,9 @@ dependencies = [
"chrono",
"clap",
"clap_complete",
"env_logger",
"futures-util",
"glob",
"ignore",
"log",
"mdbook-core",
"mdbook-driver",
"mdbook-html",
@ -1331,6 +1297,8 @@ dependencies = [
"tokio",
"toml",
"tower-http",
"tracing",
"tracing-subscriber",
"walkdir",
]
@ -1339,12 +1307,12 @@ name = "mdbook-core"
version = "0.5.0-alpha.1"
dependencies = [
"anyhow",
"log",
"regex",
"serde",
"serde_json",
"tempfile",
"toml",
"tracing",
]
[[package]]
@ -1353,7 +1321,6 @@ version = "0.5.0-alpha.1"
dependencies = [
"anyhow",
"indexmap",
"log",
"mdbook-core",
"mdbook-html",
"mdbook-markdown",
@ -1367,6 +1334,7 @@ dependencies = [
"tempfile",
"toml",
"topological-sort",
"tracing",
]
[[package]]
@ -1379,7 +1347,6 @@ dependencies = [
"font-awesome-as-a-crate",
"handlebars",
"hex",
"log",
"mdbook-core",
"mdbook-markdown",
"mdbook-renderer",
@ -1391,15 +1358,16 @@ dependencies = [
"sha2",
"tempfile",
"toml",
"tracing",
]
[[package]]
name = "mdbook-markdown"
version = "0.5.0-alpha.1"
dependencies = [
"log",
"pulldown-cmark",
"regex",
"tracing",
]
[[package]]
@ -1437,11 +1405,11 @@ name = "mdbook-summary"
version = "0.5.0-alpha.1"
dependencies = [
"anyhow",
"log",
"mdbook-core",
"memchr",
"pulldown-cmark",
"serde",
"tracing",
]
[[package]]
@ -1544,6 +1512,15 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "nu-ansi-term"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "num-modular"
version = "0.6.1"
@ -1781,21 +1758,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "portable-atomic"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "potential_utf"
version = "0.1.2"
@ -2126,6 +2088,15 @@ dependencies = [
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
[[package]]
name = "shlex"
version = "1.3.0"
@ -2333,6 +2304,15 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "tinystr"
version = "0.8.1"
@ -2504,9 +2484,21 @@ checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
@ -2514,6 +2506,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]]
@ -2598,6 +2620,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "version_check"
version = "0.9.5"

View file

@ -32,7 +32,6 @@ chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
clap = { version = "4.5.41", features = ["cargo", "wrap_help"] }
clap_complete = "4.5.55"
elasticlunr-rs = "3.0.2"
env_logger = "0.11.8"
font-awesome-as-a-crate = "0.3.0"
futures-util = "0.3.31"
glob = "0.3.3"
@ -40,7 +39,6 @@ handlebars = "6.3.2"
hex = "0.4.3"
indexmap = "2.10.0"
ignore = "0.4.23"
log = "0.4.27"
mdbook-core = { path = "crates/mdbook-core" }
mdbook-driver = { path = "crates/mdbook-driver" }
mdbook-html = { path = "crates/mdbook-html" }
@ -68,6 +66,8 @@ tokio = "1.46.1"
toml = "0.9.2"
topological-sort = "0.2.2"
tower-http = "0.6.6"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
walkdir = "2.5.0"
[package]
@ -93,8 +93,6 @@ anyhow.workspace = true
chrono.workspace = true
clap.workspace = true
clap_complete.workspace = true
env_logger.workspace = true
log.workspace = true
mdbook-core.workspace = true
mdbook-driver.workspace = true
mdbook-html.workspace = true
@ -104,6 +102,8 @@ mdbook-renderer.workspace = true
mdbook-summary.workspace = true
opener.workspace = true
toml.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
# Watch feature
ignore = { workspace = true, optional = true }

View file

@ -9,11 +9,11 @@ rust-version.workspace = true
[dependencies]
anyhow.workspace = true
log.workspace = true
regex.workspace = true
serde.workspace = true
serde_json.workspace = true
toml.workspace = true
tracing.workspace = true
[dev-dependencies]
tempfile.workspace = true

View file

@ -46,7 +46,6 @@
use crate::utils::TomlExt;
use crate::utils::log_backtrace;
use anyhow::{Context, Error, Result, bail};
use log::{debug, trace};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::env;
@ -56,6 +55,7 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use toml::Value;
use toml::value::Table;
use tracing::{debug, trace};
/// The overall configuration object for MDBook, essentially an in-memory
/// representation of `book.toml`.
@ -162,6 +162,10 @@ impl Config {
env::vars().filter_map(|(key, value)| parse_env(&key).map(|index| (index, value)));
for (key, value) in overrides {
if key == "log" {
// MDBOOK_LOG is used to control logging.
continue;
}
trace!("{} => {}", key, value);
let parsed_value = serde_json::from_str(&value)
.unwrap_or_else(|_| serde_json::Value::String(value.to_string()));

View file

@ -1,10 +1,10 @@
//! Filesystem utilities and helpers.
use anyhow::{Context, Result};
use log::{debug, trace};
use std::fs::{self, File};
use std::io::Write;
use std::path::{Component, Path, PathBuf};
use tracing::{debug, trace};
/// Naively replaces any path separator with a forward-slash '/'
pub fn normalize_path(path: &str) -> String {

View file

@ -1,11 +1,12 @@
//! Various helpers and utilities.
use anyhow::Error;
use log::error;
use regex::Regex;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use std::sync::LazyLock;
use tracing::error;
pub mod fs;
mod string;
@ -79,11 +80,13 @@ pub fn unique_id_from_content(content: &str, id_counter: &mut HashMap<String, us
/// Prints a "backtrace" of some `Error`.
pub fn log_backtrace(e: &Error) {
error!("Error: {}", e);
let mut message = format!("{e}");
for cause in e.chain().skip(1) {
error!("\tCaused By: {}", cause);
write!(message, "\n\tCaused by: {cause}").unwrap();
}
error!("{message}");
}
/// Escape `<` and `>` for HTML.

View file

@ -10,7 +10,6 @@ rust-version.workspace = true
[dependencies]
anyhow.workspace = true
indexmap.workspace = true
log.workspace = true
mdbook-core.workspace = true
mdbook-html.workspace = true
mdbook-markdown.workspace = true
@ -24,6 +23,7 @@ shlex.workspace = true
tempfile.workspace = true
toml.workspace = true
topological-sort.workspace = true
tracing.workspace = true
[lints]
workspace = true

View file

@ -1,10 +1,10 @@
use anyhow::{Context, Result, ensure};
use log::{debug, trace, warn};
use mdbook_core::book::Book;
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
use std::io::Write;
use std::path::PathBuf;
use std::process::{Child, Stdio};
use tracing::{debug, trace, warn};
/// A custom preprocessor which will shell out to a 3rd-party program.
///

View file

@ -1,9 +1,9 @@
use anyhow::Result;
use log::warn;
use mdbook_core::book::{Book, BookItem};
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
use regex::Regex;
use std::{path::Path, sync::LazyLock};
use tracing::warn;
/// A preprocessor for converting file name `README.md` to `index.md` since
/// `README.md` is the de facto index file in markdown-based documentation.

View file

@ -1,5 +1,4 @@
use anyhow::{Context, Result};
use log::{error, warn};
use mdbook_core::book::{Book, BookItem};
use mdbook_core::utils::{
take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,
@ -11,6 +10,7 @@ use std::fs;
use std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeTo};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::{error, warn};
const ESCAPE_CHAR: char = '\\';
const MAX_LINK_NESTED_DEPTH: usize = 10;

View file

@ -1,9 +1,9 @@
use anyhow::{Context, Result};
use log::trace;
use mdbook_core::book::BookItem;
use mdbook_core::utils;
use mdbook_renderer::{RenderContext, Renderer};
use std::fs;
use tracing::trace;
/// A renderer to output the Markdown after the preprocessors have run. Mostly useful
/// when debugging preprocessors.

View file

@ -3,10 +3,10 @@
//! The HTML renderer can be found in the [`mdbook_html`] crate.
use anyhow::{Context, Result, bail};
use log::{error, info, trace, warn};
use mdbook_renderer::{RenderContext, Renderer};
use std::fs;
use std::process::Stdio;
use tracing::{error, info, trace, warn};
pub use self::markdown_renderer::MarkdownRenderer;
@ -30,7 +30,7 @@ mod markdown_renderer;
/// whatever it wants, to avoid spamming users it is recommended to avoid
/// unnecessary output.
///
/// To help choose the appropriate output level, the `RUST_LOG` environment
/// To help choose the appropriate output level, the `MDBOOK_LOG` environment
/// variable will be passed through to the subprocess, if set.
///
/// If the subprocess wishes to indicate that rendering failed, it should exit

View file

@ -6,10 +6,10 @@ use std::path::PathBuf;
use super::MDBook;
use anyhow::{Context, Result};
use log::{debug, error, info, trace};
use mdbook_core::config::Config;
use mdbook_core::utils::fs::write_file;
use mdbook_html::theme;
use tracing::{debug, error, info, trace};
/// A helper for setting up a new book and its directory structure.
#[derive(Debug, Clone, PartialEq)]

View file

@ -65,12 +65,12 @@ mod load;
mod mdbook;
use anyhow::{Context, Result, bail};
use log::{error, warn};
pub use mdbook::MDBook;
pub use mdbook_core::{book, config, errors};
use shlex::Shlex;
use std::path::{Path, PathBuf};
use std::process::Command;
use tracing::{error, warn};
/// Creates a [`Command`] for command renderers and preprocessors.
fn compose_command(cmd: &str, root: &Path) -> Result<Command> {

View file

@ -1,5 +1,4 @@
use anyhow::{Context, Result};
use log::debug;
use mdbook_core::book::{Book, BookItem, Chapter};
use mdbook_core::config::BuildConfig;
use mdbook_core::utils::bracket_escape;
@ -7,6 +6,7 @@ use mdbook_summary::{Link, Summary, SummaryItem, parse_summary};
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use tracing::debug;
/// Load a book into memory from its `src/` directory.
pub(crate) fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book> {

View file

@ -6,7 +6,6 @@ use crate::init::BookBuilder;
use crate::load::{load_book, load_book_from_disk};
use anyhow::{Context, Error, Result, bail};
use indexmap::IndexMap;
use log::{debug, error, info, log_enabled, trace, warn};
use mdbook_core::book::{Book, BookItem, BookItems};
use mdbook_core::config::{Config, RustEdition};
use mdbook_core::utils;
@ -21,6 +20,7 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use tempfile::Builder as TempFileBuilder;
use topological_sort::TopologicalSort;
use tracing::{debug, error, info, trace, warn};
#[cfg(test)]
mod tests;
@ -58,7 +58,7 @@ impl MDBook {
config.update_from_env();
if log_enabled!(log::Level::Trace) {
if tracing::enabled!(tracing::Level::TRACE) {
for line in format!("Config: {config:#?}").lines() {
trace!("{}", line);
}

View file

@ -14,7 +14,6 @@ elasticlunr-rs = { workspace = true, optional = true }
font-awesome-as-a-crate.workspace = true
handlebars.workspace = true
hex.workspace = true
log.workspace = true
mdbook-core.workspace = true
mdbook-markdown.workspace = true
mdbook-renderer.workspace = true
@ -23,6 +22,7 @@ regex.workspace = true
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
tracing.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true

View file

@ -3,7 +3,6 @@ use super::static_files::StaticFiles;
use crate::theme::Theme;
use anyhow::{Context, Result, bail};
use handlebars::Handlebars;
use log::{debug, info, trace, warn};
use mdbook_core::book::{Book, BookItem, Chapter};
use mdbook_core::config::{BookConfig, Code, Config, HtmlConfig, Playground, RustEdition};
use mdbook_core::utils;
@ -18,6 +17,8 @@ use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::error;
use tracing::{debug, info, trace, warn};
/// The HTML renderer for mdBook.
#[derive(Default)]
@ -286,7 +287,7 @@ impl HtmlHandlebars {
return Ok(());
}
log::debug!("Emitting redirects");
debug!("Emitting redirects");
let redirects = combine_fragment_redirects(redirects);
for (original, (dest, fragment_map)) in redirects {
@ -306,7 +307,7 @@ impl HtmlHandlebars {
destination."
);
}
log::debug!("Redirecting \"{}\"\"{}\"", original, dest);
debug!("Redirecting \"{}\"\"{}\"", original, dest);
self.emit_redirect(handlebars, &filename, &dest, &fragment_map)?;
}
@ -1034,7 +1035,7 @@ fn combine_fragment_redirects(redirects: &HashMap<String, String>) -> CombinedRe
if let Some((source_path, source_fragment)) = original.rsplit_once('#') {
let e = combined.entry(source_path.to_string()).or_default();
if let Some(old) = e.1.insert(format!("#{source_fragment}"), new.clone()) {
log::error!(
error!(
"internal error: found duplicate fragment redirect \
{old} for {source_path}#{source_fragment}"
);

View file

@ -2,8 +2,8 @@ use font_awesome_as_a_crate as fa;
use handlebars::{
Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason,
};
use log::trace;
use std::str::FromStr;
use tracing::trace;
pub(crate) fn fa_helper(
h: &Helper<'_>,

View file

@ -2,7 +2,6 @@ use super::static_files::StaticFiles;
use crate::theme::searcher;
use anyhow::{Context, Result, bail};
use elasticlunr::{Index, IndexBuilder};
use log::{debug, warn};
use mdbook_core::book::{Book, BookItem, Chapter};
use mdbook_core::config::{Search, SearchChapterSettings};
use mdbook_core::utils;
@ -14,6 +13,7 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::{debug, warn};
const MAX_WORD_LENGTH_TO_INDEX: usize = 80;

View file

@ -3,7 +3,6 @@
use super::helpers::resources::ResourceHelper;
use crate::theme::{self, Theme, playground_editor};
use anyhow::{Context, Result};
use log::debug;
use mdbook_core::config::HtmlConfig;
use mdbook_core::utils;
use std::borrow::Cow;
@ -11,6 +10,7 @@ use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::debug;
/// Map static files to their final names and contents.
///

View file

@ -1,10 +1,10 @@
#![allow(missing_docs)]
use anyhow::Result;
use log::warn;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tracing::{info, warn};
pub mod fonts;
pub mod playground_editor;
@ -133,7 +133,7 @@ impl Theme {
if entry.file_name() == "fonts.css" {
None
} else if entry.file_type().ok()?.is_dir() {
log::info!("skipping font directory {:?}", entry.path());
info!("skipping font directory {:?}", entry.path());
None
} else {
Some(entry.path())

View file

@ -8,9 +8,9 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
log.workspace = true
pulldown-cmark.workspace = true
regex.workspace = true
tracing.workspace = true
[lints]
workspace = true

View file

@ -15,6 +15,7 @@ use std::collections::HashMap;
use std::fmt::Write;
use std::path::Path;
use std::sync::LazyLock;
use tracing::warn;
#[doc(inline)]
pub use pulldown_cmark;
@ -126,7 +127,7 @@ pub fn render_markdown(text: &str, options: &HtmlRenderOptions<'_>) -> String {
Event::Start(Tag::FootnoteDefinition(name)) => {
prev_was_footnote = false;
if !in_footnote.is_empty() {
log::warn!(
warn!(
"internal bug: nested footnote not expected in {:?}",
options.path
);
@ -139,7 +140,7 @@ pub fn render_markdown(text: &str, options: &HtmlRenderOptions<'_>) -> String {
let name = std::mem::take(&mut in_footnote_name);
if footnote_defs.contains_key(&name) {
log::warn!(
warn!(
"footnote `{name}` in {} defined multiple times - \
not updating to new definition",
options.path.display()
@ -212,7 +213,7 @@ fn add_footnote_defs(
// Remove unused.
defs.retain(|(name, _)| {
if !numbers.contains_key(name) {
log::warn!(
warn!(
"footnote `{name}` in `{}` is defined but not referenced",
options.path.display()
);

View file

@ -9,11 +9,11 @@ rust-version.workspace = true
[dependencies]
anyhow.workspace = true
log.workspace = true
mdbook-core.workspace = true
memchr.workspace = true
pulldown-cmark.workspace = true
serde.workspace = true
tracing.workspace = true
[lints]
workspace = true

View file

@ -5,7 +5,6 @@
//! file structure for [mdBook](https://rust-lang.github.io/mdBook/).
use anyhow::{Context, Error, Result, bail};
use log::{debug, trace, warn};
pub use mdbook_core::book::SectionNumber;
use memchr::Memchr;
use pulldown_cmark::{DefaultBrokenLinkCallback, Event, HeadingLevel, Tag, TagEnd};
@ -13,6 +12,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt::Display;
use std::path::{Path, PathBuf};
use tracing::{debug, trace, warn};
/// Parse the text from a `SUMMARY.md` file into a sort of "recipe" to be
/// used when loading a book from disk.

View file

@ -317,7 +317,7 @@ the "rule of silence" and only generate output when necessary (e.g. an error in
generation or a warning).
All environment variables are passed through to the backend, allowing you to use
the usual `RUST_LOG` to control logging verbosity.
the usual `MDBOOK_LOG` to control logging verbosity.
## Wrapping up

View file

@ -2,6 +2,7 @@ use super::command_prelude::*;
use crate::{get_book_dir, open};
use anyhow::Result;
use mdbook_driver::MDBook;
use tracing::error;
// Create clap subcommand arguments
pub fn make_subcommand() -> Command {

View file

@ -6,6 +6,7 @@ use mdbook_driver::MDBook;
use std::io;
use std::io::Write;
use std::process::Command;
use tracing::debug;
// Create clap subcommand arguments
pub fn make_subcommand() -> ClapCommand {

View file

@ -15,6 +15,7 @@ use std::net::{SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use tokio::sync::broadcast;
use tower_http::services::{ServeDir, ServeFile};
use tracing::{error, info, trace};
/// The HTTP endpoint for the websocket used to trigger reloads when a file changes.
const LIVE_RELOAD_ENDPOINT: &str = "__livereload";

View file

@ -3,6 +3,7 @@ use crate::{get_book_dir, open};
use anyhow::Result;
use mdbook_driver::MDBook;
use std::path::{Path, PathBuf};
use tracing::error;
mod native;
mod poller;

View file

@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::thread::sleep;
use std::time::Duration;
use tracing::{error, info, warn};
pub fn rebuild_on_change(
book_dir: &Path,
@ -71,7 +72,7 @@ pub fn rebuild_on_change(
.filter_map(|event| match event {
Ok(events) => Some(events),
Err(error) => {
log::warn!("error while watching for changes: {error}");
warn!("error while watching for changes: {error}");
None
}
})

View file

@ -11,6 +11,7 @@ use std::collections::HashMap;
use std::fs::FileType;
use std::path::{Path, PathBuf};
use std::time::{Duration, Instant, SystemTime};
use tracing::{debug, error, info, trace, warn};
use walkdir::WalkDir;
/// Calls the closure when a book source file is changed, blocking indefinitely.

View file

@ -4,20 +4,15 @@
#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
use anyhow::anyhow;
use chrono::Local;
use clap::{Arg, ArgMatches, Command};
use clap_complete::Shell;
use env_logger::Builder;
use log::LevelFilter;
use mdbook_core::utils;
use std::env;
use std::ffi::OsStr;
use std::io::Write;
use std::path::PathBuf;
use tracing::{error, info};
mod cmd;
@ -99,29 +94,36 @@ fn create_clap_command() -> Command {
}
fn init_logger() {
let mut builder = Builder::new();
builder.format(|formatter, record| {
writeln!(
formatter,
"{} [{}] ({}): {}",
Local::now().format("%Y-%m-%d %H:%M:%S"),
record.level(),
record.target(),
record.args()
)
});
if let Ok(var) = env::var("RUST_LOG") {
builder.parse_filters(&var);
let filter = tracing_subscriber::EnvFilter::builder()
.with_env_var("MDBOOK_LOG")
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy();
let log_env = std::env::var("MDBOOK_LOG");
// Silence some particularly noisy dependencies unless the user
// specifically asks for them.
let silence_unless_specified = |filter: tracing_subscriber::EnvFilter, target| {
if !log_env.as_ref().map_or(false, |s| {
s.split(',').any(|directive| directive.starts_with(target))
}) {
filter.add_directive(format!("{target}=warn").parse().unwrap())
} else {
// if no RUST_LOG provided, default to logging at the Info level
builder.filter(None, LevelFilter::Info);
// Filter extraneous html5ever not-implemented messages
builder.filter(Some("html5ever"), LevelFilter::Error);
filter
}
};
let filter = silence_unless_specified(filter, "handlebars");
let filter = silence_unless_specified(filter, "html5ever");
builder.init();
// Don't show the target by default, since it generally isn't useful
// unless you are overriding the level.
let with_target = log_env.is_ok();
tracing_subscriber::fmt()
.without_time()
.with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
.with_writer(std::io::stderr)
.with_env_filter(filter)
.with_target(with_target)
.init();
}
fn get_book_dir(args: &ArgMatches) -> PathBuf {

View file

@ -360,7 +360,7 @@ impl BookCommand {
/// Use this to debug a command.
///
/// Pass the value that you would normally pass to `RUST_LOG`, and this
/// Pass the value that you would normally pass to `MDBOOK_LOG`, and this
/// will enable logging, print the command that runs and its output.
///
/// This will fail if you use it in CI.
@ -378,7 +378,7 @@ impl BookCommand {
let mut cmd = Command::new(env!("CARGO_BIN_EXE_mdbook"));
cmd.current_dir(&self.dir)
.args(&self.args)
.env_remove("RUST_LOG")
.env_remove("MDBOOK_LOG")
// Don't read the system git config which is out of our control.
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_CONFIG_GLOBAL", &self.dir)
@ -389,7 +389,7 @@ impl BookCommand {
.env_remove("GIT_COMMITTER_NAME");
if let Some(debug) = &self.debug {
cmd.env("RUST_LOG", debug);
cmd.env("MDBOOK_LOG", debug);
}
for (k, v) in &self.env {
@ -479,23 +479,9 @@ static LITERAL_REDACTIONS: &[(&str, &str)] = &[
("[EXE]", std::env::consts::EXE_SUFFIX),
];
/// This makes it easier to write regex replacements that are guaranteed to only
/// get compiled once
macro_rules! regex {
($re:literal $(,)?) => {{
static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
RE.get_or_init(|| regex::Regex::new($re).unwrap())
}};
}
fn assert(root: &Path) -> snapbox::Assert {
let mut subs = snapbox::Redactions::new();
subs.insert("[ROOT]", root.to_path_buf()).unwrap();
subs.insert(
"[TIMESTAMP]",
regex!(r"(?m)(?<redacted>20\d\d-\d{2}-\d{2} \d{2}:\d{2}:\d{2})"),
)
.unwrap();
subs.insert("[VERSION]", mdbook_core::MDBOOK_VERSION)
.unwrap();

View file

@ -10,9 +10,9 @@ use crate::prelude::*;
fn basic_build() {
BookTest::from_dir("build/basic_build").run("build", |cmd| {
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
});
@ -24,8 +24,8 @@ fn basic_build() {
fn failure_on_missing_file() {
BookTest::from_dir("build/missing_file").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Chapter file not found, ./chapter_1.md
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [NOT_FOUND]
ERROR Chapter file not found, ./chapter_1.md
[TAB]Caused by: [NOT_FOUND]
"#]]);
});
@ -46,10 +46,10 @@ fn create_missing() {
fn no_reserved_filename() {
BookTest::from_dir("build/no_reserved_filename").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: print.md is reserved for internal use
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: print.md is reserved for internal use
"#]]);
});
@ -77,9 +77,9 @@ fn dest_dir_relative_path() {
cmd.args(&["--dest-dir", "foo", ".."])
.current_dir(&current_dir)
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/work/foo`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/work/foo`
"#]]);
});

View file

@ -120,8 +120,8 @@ fn bad_config_top_level() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Invalid configuration file
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: TOML parse error at line 1, column 1
ERROR Invalid configuration file
[TAB]Caused by: TOML parse error at line 1, column 1
|
1 | foo = 123
| ^^^
@ -145,8 +145,8 @@ fn bad_config_top_level_table() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Invalid configuration file
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: TOML parse error at line 1, column 2
ERROR Invalid configuration file
[TAB]Caused by: TOML parse error at line 1, column 2
|
1 | [other]
| ^^^^^
@ -171,8 +171,8 @@ fn bad_config_in_book_table() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Invalid configuration file
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: TOML parse error at line 3, column 1
ERROR Invalid configuration file
[TAB]Caused by: TOML parse error at line 3, column 1
|
3 | foo = 123
| ^^^
@ -196,8 +196,8 @@ fn bad_config_in_rust_table() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Invalid configuration file
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: TOML parse error at line 2, column 1
ERROR Invalid configuration file
[TAB]Caused by: TOML parse error at line 2, column 1
|
2 | title = "bad-config"
| ^^^^^

View file

@ -45,10 +45,10 @@ fn recursive_include() {
BookTest::from_dir("includes/all_includes")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [ERROR] (mdbook_driver::builtin_preprocessors::links): Stack depth exceeded in recursive.md. Check for cyclic includes
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
ERROR Stack depth exceeded in recursive.md. Check for cyclic includes
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
})

View file

@ -19,7 +19,7 @@ All done, no errors...
"#]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::init): Creating a new book with stub content
INFO Creating a new book with stub content
"#]]);
})
@ -84,7 +84,7 @@ All done, no errors...
"#]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::init): Creating a new book with stub content
INFO Creating a new book with stub content
"#]]);
})
@ -115,7 +115,7 @@ All done, no errors...
"#]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::init): Creating a new book with stub content
INFO Creating a new book with stub content
"#]])
.args(&["--title", "Example title"]);

View file

@ -20,13 +20,13 @@ fn footnotes() {
BookTest::from_dir("markdown/footnotes")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [WARN] (mdbook_markdown): footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition
[TIMESTAMP] [WARN] (mdbook_markdown): footnote `unused` in `footnotes.md` is defined but not referenced
[TIMESTAMP] [WARN] (mdbook_markdown): footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition
[TIMESTAMP] [WARN] (mdbook_markdown): footnote `unused` in `footnotes.md` is defined but not referenced
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
WARN footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition
WARN footnote `unused` in `footnotes.md` is defined but not referenced
WARN footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition
WARN footnote `unused` in `footnotes.md` is defined but not referenced
INFO HTML book written to `[ROOT]/book`
"#]]);
})

View file

@ -49,9 +49,9 @@ fn runs_preprocessors() {
fn nop_preprocessor() {
BookTest::from_dir("preprocessor/nop_preprocessor").run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
});
@ -60,14 +60,13 @@ fn nop_preprocessor() {
// Failing preprocessor generates an error.
#[test]
fn failing_preprocessor() {
BookTest::from_dir("preprocessor/failing_preprocessor")
.run("build", |cmd| {
BookTest::from_dir("preprocessor/failing_preprocessor").run("build", |cmd| {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
INFO Book building has started
Boom!!1!
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: The "nop-preprocessor" preprocessor exited unsuccessfully with [EXIT_STATUS]: 1 status
ERROR The "nop-preprocessor" preprocessor exited unsuccessfully with [EXIT_STATUS]: 1 status
"#]]);
});
@ -128,9 +127,9 @@ fn relative_command_path() {
)
.run("build", |cmd| {
cmd.expect_stdout(str![""]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
})
@ -143,9 +142,9 @@ fn relative_command_path() {
cmd.current_dir(cmd.dir.join("src"))
.expect_stdout(str![""])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/src/../book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/src/../book`
"#]]);
})
@ -160,10 +159,10 @@ fn missing_preprocessor() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [ERROR] (mdbook_driver): The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` preprocessor installed? If you want to ignore this error when the `missing` preprocessor is not installed, set `optional = true` in the `[preprocessor.missing]` section of the book.toml configuration file.
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Unable to run the preprocessor `missing`
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [NOT_FOUND]
INFO Book building has started
ERROR The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` preprocessor installed? If you want to ignore this error when the `missing` preprocessor is not installed, set `optional = true` in the `[preprocessor.missing]` section of the book.toml configuration file.
ERROR Unable to run the preprocessor `missing`
[TAB]Caused by: [NOT_FOUND]
"#]]);
});
@ -174,10 +173,10 @@ fn missing_preprocessor() {
fn missing_optional_not_fatal() {
BookTest::from_dir("preprocessor/missing_optional_not_fatal").run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [WARN] (mdbook_driver): The command `trduyvbhijnorgevfuhn` for preprocessor `missing` was not found, but is marked as optional.
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
WARN The command `trduyvbhijnorgevfuhn` for preprocessor `missing` was not found, but is marked as optional.
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
});
@ -239,14 +238,14 @@ fn extension_compatibility() {
// that the built book is identical with the preprocessor enabled.
test.run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [WARN] (mdbook_driver): The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book/html`
[TIMESTAMP] [WARN] (mdbook_driver): The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the my-renderer backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "my-renderer" renderer
[TIMESTAMP] [WARN] (mdbook_driver): The command `./my-renderer` for backend `my-renderer` was not found, but is marked as optional.
INFO Book building has started
WARN The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.
INFO Running the html backend
INFO HTML book written to `[ROOT]/book/html`
WARN The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.
INFO Running the my-renderer backend
INFO Invoking the "my-renderer" renderer
WARN The command `./my-renderer` for backend `my-renderer` was not found, but is marked as optional.
"#]]);
});
@ -441,11 +440,11 @@ fn extension_compatibility() {
)
.run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book/html`
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the my-renderer backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "my-renderer" renderer
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book/html`
INFO Running the my-renderer backend
INFO Invoking the "my-renderer" renderer
"#]]);
})

View file

@ -22,11 +22,11 @@ fn redirects_are_emitted_correctly() {
fn redirect_removed_with_fragments_only() {
BookTest::from_dir("redirects/redirect_removed_with_fragments_only").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: Unable to emit redirects
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: redirect entry for `old-file.html` only has source paths with `#` fragments
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: Unable to emit redirects
[TAB]Caused by: redirect entry for `old-file.html` only has source paths with `#` fragments
There must be an entry without the `#` fragment to determine the default destination.
"#]]);
@ -38,10 +38,10 @@ There must be an entry without the `#` fragment to determine the default destina
fn redirect_existing_page() {
BookTest::from_dir("redirects/redirect_existing_page").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: redirect found for existing chapter at `/chapter_1.html`
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: redirect found for existing chapter at `/chapter_1.html`
Either delete the redirect or remove the chapter.
"#]]);

View file

@ -64,12 +64,12 @@ fn failing_command() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the failing backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "failing" renderer
[TIMESTAMP] [ERROR] (mdbook_driver::builtin_renderers): Renderer exited with non-zero return code.
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: The "failing" renderer failed
INFO Book building has started
INFO Running the failing backend
INFO Invoking the "failing" renderer
ERROR Renderer exited with non-zero return code.
ERROR Rendering failed
[TAB]Caused by: The "failing" renderer failed
"#]]);
});
@ -82,13 +82,13 @@ fn missing_renderer() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the missing backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "missing" renderer
[TIMESTAMP] [ERROR] (mdbook_driver): The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` backend installed? If you want to ignore this error when the `missing` backend is not installed, set `optional = true` in the `[output.missing]` section of the book.toml configuration file.
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: Unable to run the backend `missing`
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [NOT_FOUND]
INFO Book building has started
INFO Running the missing backend
INFO Invoking the "missing" renderer
ERROR The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` backend installed? If you want to ignore this error when the `missing` backend is not installed, set `optional = true` in the `[output.missing]` section of the book.toml configuration file.
ERROR Rendering failed
[TAB]Caused by: Unable to run the backend `missing`
[TAB]Caused by: [NOT_FOUND]
"#]]);
});
@ -99,10 +99,10 @@ fn missing_renderer() {
fn missing_optional_not_fatal() {
BookTest::from_dir("renderer/missing_optional_not_fatal").run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the missing backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "missing" renderer
[TIMESTAMP] [WARN] (mdbook_driver): The command `trduyvbhijnorgevfuhn` for backend `missing` was not found, but is marked as optional.
INFO Book building has started
INFO Running the missing backend
INFO Invoking the "missing" renderer
WARN The command `trduyvbhijnorgevfuhn` for backend `missing` was not found, but is marked as optional.
"#]]);
});
@ -131,9 +131,9 @@ Hello World!
"#]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the arguments backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "arguments" renderer
INFO Book building has started
INFO Running the arguments backend
INFO Invoking the "arguments" renderer
"#]]);
});
@ -156,9 +156,9 @@ fn backends_receive_render_context_via_stdin() {
)
.run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the cat-to-file backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "cat-to-file" renderer
INFO Book building has started
INFO Running the cat-to-file backend
INFO Invoking the "cat-to-file" renderer
"#]]);
})
@ -233,9 +233,9 @@ fn relative_command_path() {
)
.run("build", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the myrenderer backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "myrenderer" renderer
INFO Book building has started
INFO Running the myrenderer backend
INFO Invoking the "myrenderer" renderer
"#]]);
})

View file

@ -126,10 +126,10 @@ fn with_no_source_path() {
fn chapter_settings_validation_error() {
BookTest::from_dir("search/chapter_settings_validation_error").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [output.html.search.chapter] key `does-not-exist` does not match any chapter paths
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: [output.html.search.chapter] key `does-not-exist` does not match any chapter paths
"#]]);
});

View file

@ -7,9 +7,9 @@ use crate::prelude::*;
fn passing_tests() {
BookTest::from_dir("test/passing_tests").run("test", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Intro': "intro.md"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Passing 1': "passing1.md"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Passing 2': "passing2.md"
INFO Testing chapter 'Intro': "intro.md"
INFO Testing chapter 'Passing 1': "passing1.md"
INFO Testing chapter 'Passing 2': "passing2.md"
"#]]);
});
@ -27,8 +27,8 @@ fn failing_tests() {
// still includes a little bit of output, so if that is a problem,
// add more redactions.
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Failing Tests': "failing.md"
[TIMESTAMP] [ERROR] (mdbook_driver::mdbook): rustdoc returned an error:
INFO Testing chapter 'Failing Tests': "failing.md"
ERROR rustdoc returned an error:
--- stdout
@ -38,8 +38,8 @@ test failing.md - Failing_Tests (line 3) ... FAILED
thread [..] panicked at failing.md:3:1:
fail
...
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Failing Include': "failing_include.md"
[TIMESTAMP] [ERROR] (mdbook_driver::mdbook): rustdoc returned an error:
INFO Testing chapter 'Failing Include': "failing_include.md"
ERROR rustdoc returned an error:
--- stdout
...
@ -48,7 +48,7 @@ test failing_include.md - Failing_Include (line 3) ... FAILED
thread [..] panicked at failing_include.md:3:1:
failing!
...
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: One or more tests failed
ERROR One or more tests failed
"#]]);
});
@ -62,14 +62,14 @@ fn test_individual_chapter() {
cmd.args(&["Passing 1"])
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Passing 1': "passing1.md"
INFO Testing chapter 'Passing 1': "passing1.md"
"#]]);
})
// Can also be a source path.
.run("test -c passing2.md", |cmd| {
cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Testing chapter 'Passing 2': "passing2.md"
INFO Testing chapter 'Passing 2': "passing2.md"
"#]]);
});
@ -82,7 +82,7 @@ fn chapter_not_found() {
cmd.expect_failure()
.expect_stdout(str![[""]])
.expect_stderr(str![[r#"
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Chapter not found: bogus
ERROR Chapter not found: bogus
"#]]);
});

View file

@ -5,14 +5,12 @@ use crate::prelude::*;
// Checks what happens if the theme directory is missing.
#[test]
fn missing_theme() {
BookTest::from_dir("theme/missing_theme")
.run("build", |cmd| {
cmd.expect_failure()
.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: theme dir [ROOT]/./non-existent-directory does not exist
BookTest::from_dir("theme/missing_theme").run("build", |cmd| {
cmd.expect_failure().expect_stderr(str![[r#"
INFO Book building has started
INFO Running the html backend
ERROR Rendering failed
[TAB]Caused by: theme dir [ROOT]/./non-existent-directory does not exist
"#]]);
});
@ -24,9 +22,9 @@ fn empty_theme() {
BookTest::from_dir("theme/empty_theme").run("build", |cmd| {
std::fs::create_dir(cmd.dir.join("theme")).unwrap();
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
});
@ -147,9 +145,9 @@ fn empty_fonts_css() {
BookTest::from_dir("theme/empty_fonts_css")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
})
@ -163,9 +161,9 @@ fn custom_fonts_css() {
BookTest::from_dir("theme/custom_fonts_css")
.run("build", |cmd| {
cmd.expect_stderr(str![[r#"
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the html backend
[TIMESTAMP] [INFO] (mdbook_html::html_handlebars::hbs_renderer): HTML book written to `[ROOT]/book`
INFO Book building has started
INFO Running the html backend
INFO HTML book written to `[ROOT]/book`
"#]]);
})