// now: Nix-based distributed command runner
// Copyright (C) 2026 Eric Rodrigues Pires
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
// more details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see .
use std::{
hash::{Hash, Hasher},
io::Write,
marker::PhantomData,
};
use owo_colors::{OwoColorize, Style};
use rand::{SeedableRng, seq::IndexedRandom};
use tracing::{Subscriber, field::Visit};
use tracing_subscriber::{Layer, field::VisitOutput, registry::LookupSpan};
use crate::{command::LoggingLevel, utils::trim_string};
struct CachedBuilder {
short_name: String,
style: Style,
}
pub(crate) struct NowSubscriberLayer std::io::Stderr> {
make_writer: W,
builder_name_limit: usize,
cache: papaya::HashMap,
hasher: ahash::RandomState,
logging_level: LoggingLevel,
_subscriber: PhantomData,
}
impl Default for NowSubscriberLayer {
fn default() -> Self {
Self {
make_writer: std::io::stderr,
builder_name_limit: 40,
cache: papaya::HashMap::default(),
hasher: ahash::RandomState::default(),
logging_level: LoggingLevel::default(),
_subscriber: PhantomData,
}
}
}
impl NowSubscriberLayer {
pub(crate) fn with_level(self, logging_level: LoggingLevel) -> Self {
Self {
logging_level,
..self
}
}
}
impl Layer for NowSubscriberLayer
where
S: Subscriber + for<'span> LookupSpan<'span>,
W: for<'writer> tracing_subscriber::fmt::writer::MakeWriter<'writer> + 'static,
{
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut fields_visitor = NowSubscriberVisitor::new(
&self.cache,
&self.hasher,
self.builder_name_limit,
self.logging_level,
);
event.record(&mut fields_visitor);
if let Some(log_line) = fields_visitor.finish() {
let _ = writeln!(
self.make_writer.make_writer_for(event.metadata()),
"{log_line}"
);
}
}
}
struct NowSubscriberVisitor<'a> {
cache: &'a papaya::HashMap,
hasher: &'a ahash::RandomState,
builder_name_limit: usize,
logging_level: LoggingLevel,
runner: Option,
is_remote: Option,
step: Option,
message: Option,
}
impl<'a> NowSubscriberVisitor<'a> {
fn new(
cache: &'a papaya::HashMap,
hasher: &'a ahash::RandomState,
builder_name_limit: usize,
logging_level: LoggingLevel,
) -> Self {
NowSubscriberVisitor {
cache,
hasher,
builder_name_limit,
logging_level,
runner: None,
is_remote: None,
step: None,
message: None,
}
}
}
impl Visit for NowSubscriberVisitor<'_> {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn core::fmt::Debug) {
if field.name() == "message" {
self.message = Some(format!("{value:?}"));
}
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
if field.name() == "is_remote" {
self.is_remote = Some(value);
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
match field.name() {
"runner" => self.runner = Some(value.to_string()),
"step" => self.step = Some(value.to_string()),
_ => self.record_debug(field, &value),
}
}
}
impl tracing_subscriber::field::VisitOutput