1
0
Fork 0

jaeger tracing setup

This commit is contained in:
Adrian Hedqvist 2023-11-10 23:09:00 +01:00
parent e9549217ae
commit a855c0c63f
10 changed files with 395 additions and 34 deletions

345
Cargo.lock generated
View file

@ -85,6 +85,17 @@ dependencies = [
"libc",
]
[[package]]
name = "async-channel"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-compression"
version = "0.4.4"
@ -148,6 +159,7 @@ dependencies = [
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -165,6 +177,7 @@ dependencies = [
"rustversion",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -306,6 +319,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
[[package]]
name = "castaway"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]]
name = "cc"
version = "1.0.83"
@ -386,6 +405,15 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "concurrent-queue"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "config"
version = "0.13.3"
@ -458,6 +486,36 @@ dependencies = [
"typenum",
]
[[package]]
name = "curl"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
dependencies = [
"curl-sys",
"libc",
"openssl-probe",
"openssl-sys",
"schannel",
"socket2 0.4.10",
"winapi",
]
[[package]]
name = "curl-sys"
version = "0.4.65+curl-8.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
"winapi",
]
[[package]]
name = "darling"
version = "0.14.4"
@ -540,6 +598,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "eyre"
version = "0.6.8"
@ -550,6 +614,15 @@ dependencies = [
"once_cell",
]
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "flate2"
version = "1.0.28"
@ -601,6 +674,27 @@ dependencies = [
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-macro"
version = "0.3.29"
@ -867,6 +961,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "ignore"
version = "0.4.20"
@ -919,6 +1023,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "integer-encoding"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
[[package]]
name = "io-lifetimes"
version = "1.0.11"
@ -940,6 +1050,31 @@ dependencies = [
"serde",
]
[[package]]
name = "isahc"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9"
dependencies = [
"async-channel",
"castaway",
"crossbeam-utils",
"curl",
"curl-sys",
"event-listener",
"futures-lite",
"http",
"log",
"once_cell",
"polling",
"slab",
"sluice",
"tracing",
"tracing-futures",
"url",
"waker-fn",
]
[[package]]
name = "itoa"
version = "1.0.9"
@ -993,6 +1128,18 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libz-sys"
version = "1.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "line-wrap"
version = "0.1.1"
@ -1169,6 +1316,24 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "opentelemetry"
version = "0.21.0"
@ -1185,6 +1350,46 @@ dependencies = [
"urlencoding",
]
[[package]]
name = "opentelemetry-http"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f51189ce8be654f9b5f7e70e49967ed894e84a06fc35c6c042e64ac1fc5399e"
dependencies = [
"async-trait",
"bytes",
"http",
"isahc",
"opentelemetry",
]
[[package]]
name = "opentelemetry-jaeger"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1"
dependencies = [
"async-trait",
"futures-core",
"futures-util",
"http",
"isahc",
"opentelemetry",
"opentelemetry-http",
"opentelemetry-semantic-conventions",
"opentelemetry_sdk",
"thrift",
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84"
dependencies = [
"opentelemetry",
]
[[package]]
name = "opentelemetry_sdk"
version = "0.21.0"
@ -1199,12 +1404,21 @@ dependencies = [
"glob",
"once_cell",
"opentelemetry",
"ordered-float",
"ordered-float 4.1.1",
"percent-encoding",
"rand",
"thiserror",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "ordered-float"
version = "4.1.1"
@ -1236,6 +1450,12 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "parking"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1415,6 +1635,22 @@ dependencies = [
"time",
]
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys 0.48.0",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
@ -1659,6 +1895,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "schannel"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -1781,6 +2026,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "sluice"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5"
dependencies = [
"async-channel",
"futures-core",
"futures-io",
]
[[package]]
name = "smallvec"
version = "1.11.2"
@ -1914,6 +2170,28 @@ dependencies = [
"once_cell",
]
[[package]]
name = "threadpool"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [
"num_cpus",
]
[[package]]
name = "thrift"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09"
dependencies = [
"byteorder",
"integer-encoding",
"log",
"ordered-float 2.10.1",
"threadpool",
]
[[package]]
name = "time"
version = "0.3.30"
@ -1943,6 +2221,21 @@ dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.34.0"
@ -1959,6 +2252,7 @@ dependencies = [
"signal-hook-registry",
"socket2 0.5.5",
"tokio-macros",
"tracing",
"windows-sys 0.48.0",
]
@ -2136,6 +2430,16 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project",
"tracing",
]
[[package]]
name = "tracing-log"
version = "0.1.4"
@ -2284,18 +2588,44 @@ dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "url"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
@ -2317,12 +2647,24 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "walkdir"
version = "2.4.0"
@ -2423,6 +2765,7 @@ dependencies = [
"config",
"glob",
"opentelemetry",
"opentelemetry-jaeger",
"prometheus",
"pulldown-cmark",
"regex",

View file

@ -6,13 +6,14 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = { version = "0.6.12", features = ["http2", "original-uri"] }
axum = { version = "0.6.20", features = ["http2", "original-uri", "tracing"] }
cached = "0.46.1"
chrono = { version = "0.4.24", features = ["serde"] }
chrono = { version = "0.4.31", features = ["serde"] }
color-eyre = "0.6.1"
config = "0.13.3"
glob = "0.3.0"
opentelemetry = { version = "0.21.0", features = ["metrics"] }
opentelemetry-jaeger = { version = "0.20.0", features = ["collector_client", "isahc_collector_client"]}
prometheus = { version = "0.13.3", features = ["process"] }
pulldown-cmark = "0.9.2"
regex = "1.7.2"
@ -20,11 +21,11 @@ serde = "1.0.144"
serde_derive = "1.0.144"
serde_json = "1.0.85"
syntect = "5.0.0"
tera = { version = "1.17.0", features = ["builtins"] }
tokio = { version = "1.19.2", features = ["full"] }
tera = { version = "1.19.1", features = ["builtins"] }
tokio = { version = "1.34.0", features = ["full", "tracing"] }
toml = "0.8.8"
tower = { version = "0.4.12", features = ["full"] }
tower-http = { version = "0.4.0", features = ["full"] }
tower = { version = "0.4.13", features = ["full"] }
tower-http = { version = "0.4.4", features = ["full"] }
tracing = "0.1.35"
tracing-opentelemetry = "0.22.0"
tracing-subscriber = { version = "0.3.11", features = ["fmt", "env-filter", "json", "tracing-log"] }
tracing-subscriber = { version = "0.3.17", features = ["fmt", "env-filter", "json", "tracing-log"] }

View file

@ -1,3 +1,3 @@
base_url = "http://localhost:8080/"
bind_address = "0.0.0.0:8080"
logging = "info"
logging = "info,website=debug"

View file

@ -37,9 +37,7 @@ pub fn routes(state: &Arc<AppState>) -> Router<Arc<AppState>> {
.merge(posts::router())
.merge(tags::router())
.merge(posts::alias_router(state.posts.values()))
.layer(axum::middleware::from_fn(metrics_middleware))
.route("/healthcheck", get(healthcheck))
.route("/metrics", get(metrics))
.route_service(
"/posts/:slug/*path",
ServeDir::new("./"),
@ -48,6 +46,8 @@ pub fn routes(state: &Arc<AppState>) -> Router<Arc<AppState>> {
"/static/*path",
ServeDir::new("./"),
)
.layer(axum::middleware::from_fn(metrics_middleware))
.route("/metrics", get(metrics))
}
#[instrument(skip(state))]
@ -76,6 +76,7 @@ async fn healthcheck() -> &'static str {
"OK"
}
#[instrument]
async fn metrics() -> impl IntoResponse {
let encoder = TextEncoder::new();
let metric_families = prometheus::gather();
@ -93,6 +94,7 @@ pub async fn not_found() -> impl IntoResponse {
(StatusCode::NOT_FOUND, ())
}
#[instrument(skip(request, next))]
pub async fn metrics_middleware<B>(request: Request<B>, next: Next<B>) -> Response {
let path = request.uri().path().to_string();
let method = request.method().clone();

View file

@ -76,18 +76,13 @@ pub async fn index(
let res = state.tera.render("posts_index.html", &c)?;
let mut headers = vec![];
if let Some(date) = last_changed {
headers.push((header::LAST_MODIFIED, date.to_rfc2822()));
}
Ok((
StatusCode::OK,
[(
header::LAST_MODIFIED,
last_changed.map_or_else(
|| chrono::offset::Utc::now().to_rfc2822(),
|| state.startup_time.to_rfc2822(),
|d| d.to_rfc2822(),
),
)],
@ -122,7 +117,7 @@ pub async fn view(
[(
header::LAST_MODIFIED,
last_changed.map_or_else(
|| chrono::offset::Utc::now().to_rfc2822(),
|| state.startup_time.to_rfc2822(),
|d| d.to_rfc2822(),
),
)],
@ -131,6 +126,7 @@ pub async fn view(
.into_response())
}
#[instrument(skip(state))]
pub async fn feed(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
@ -154,7 +150,7 @@ pub async fn feed(
(
header::LAST_MODIFIED,
&last_changed.map_or_else(
|| chrono::offset::Utc::now().to_rfc2822(),
|| state.startup_time.to_rfc2822(),
|d| d.to_rfc2822(),
),
),

View file

@ -74,6 +74,7 @@ pub async fn view(
let ctx = TagContext { title: &title };
let mut c = tera::Context::new();
c.insert("base_url", &state.base_url.to_string());
c.insert("tag_slug", &tag);
c.insert("page", &ctx);
c.insert("posts", &posts);
@ -94,6 +95,7 @@ pub async fn view(
.into_response())
}
#[instrument(skip(state))]
pub async fn feed(
Path(slug): Path<String>,
State(state): State<Arc<AppState>>,

View file

@ -1,6 +1,7 @@
use syntect::{highlighting::ThemeSet, parsing::SyntaxSet};
use tracing::error;
use tracing::{error, instrument};
#[instrument(skip(content, lang, theme))]
pub fn hilight(content: &str, lang: &str, theme: Option<&str>) -> color_eyre::Result<String> {
let ss = SyntaxSet::load_defaults_newlines();
let s = ss

View file

@ -17,7 +17,7 @@ use post::Post;
use tag::Tag;
use tera::Tera;
use tower_http::{compression::CompressionLayer, cors::CorsLayer};
use tracing::{field::Empty, info_span, log::info, Span};
use tracing::{field::Empty, info_span, log::info, Span, instrument};
use tracing_subscriber::{prelude::*, EnvFilter};
@ -40,16 +40,25 @@ pub struct AppState {
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
let cfg = Config::builder()
.add_source(config::File::with_name("config.toml"))
.add_source(config::Environment::with_prefix("APP"))
.add_source(config::Environment::with_prefix("WEBSITE"))
.build()?;
color_eyre::install()?;
init_tracing(&cfg);
info!("Starting server...");
let app = init_app(&cfg).await?;
axum::Server::bind(&cfg.get_string("bind_address")?.parse().unwrap())
.serve(app.into_make_service())
.await?;
Ok(())
}
#[instrument]
async fn init_app(cfg: &Config) -> Result<axum::routing::Router> {
let base_url: Uri = cfg.get_string("base_url")?
.parse()
.unwrap();
@ -68,7 +77,8 @@ async fn main() -> Result<()> {
state.tags = tags;
let state = Arc::new(state);
let app = handlers::routes(&state)
info!("Listening at {}", state.base_url);
Ok(handlers::routes(&state)
.layer(CorsLayer::permissive())
.layer(CompressionLayer::new())
.layer(
@ -76,15 +86,7 @@ async fn main() -> Result<()> {
.make_span_with(make_span)
.on_response(on_response),
)
.with_state(state.clone());
info!("Now listening at {}", state.base_url);
axum::Server::bind(&cfg.get_string("bind_address")?.parse().unwrap())
.serve(app.into_make_service())
.await?;
Ok(())
.with_state(state.clone()))
}
fn init_tracing(cfg: &Config) {
@ -98,12 +100,22 @@ fn init_tracing(cfg: &Config) {
.from_env_lossy()
};
opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());
let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_service_name("website")
.install_simple().unwrap();
let otel = tracing_opentelemetry::layer().with_tracer(tracer);
tracing_subscriber::registry()
.with(filter)
.with(otel)
.with(tracing_subscriber::fmt::layer())
.init();
}
fn make_span(request: &Request<Body>) -> Span {
let uri = if let Some(OriginalUri(uri)) = request.extensions().get::<OriginalUri>() {
uri

View file

@ -6,10 +6,12 @@ use pulldown_cmark::Event;
use pulldown_cmark::Tag;
use pulldown_cmark::{Options, Parser};
use regex::Regex;
use tracing::instrument;
static STARTS_WITH_SCHEMA_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^\w+:").unwrap());
static EMAIL_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^.+?@\w+(\.\w+)*$").unwrap());
#[instrument(skip(markdown))]
pub fn render_markdown_to_html(base_uri: Option<&Uri>, markdown: &str) -> String {
let mut opt = Options::empty();
opt.insert(Options::ENABLE_FOOTNOTES);

View file

@ -1,6 +1,7 @@
use std::collections::HashMap;
use serde_derive::Serialize;
use tracing::instrument;
use crate::post::Post;
@ -11,6 +12,7 @@ pub struct Tag {
pub posts: Vec<String>,
}
#[instrument(skip(posts))]
pub fn get_tags<'a>(posts: impl IntoIterator<Item = &'a Post>) -> HashMap<String, Tag> {
let mut tags: HashMap<String, Tag> = HashMap::new();