Skip to content

Commit 758cce7

Browse files
feat(rust): add a custom log format to change the fields order
1 parent 5e31790 commit 758cce7

File tree

8 files changed

+188
-16
lines changed

8 files changed

+188
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

implementations/rust/ockam/ockam_api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ log = "0.4"
7878
miette = { version = "7.2.0", features = ["fancy-no-backtrace"] }
7979
minicbor = { version = "0.25.1", default-features = false, features = ["alloc", "derive"] }
8080
nix = { version = "0.29", features = ["signal"] }
81+
nu-ansi-term = "0.50"
8182
once_cell = { version = "1", default-features = false }
8283
open = "5.3.0"
8384
opentelemetry = { version = "0.26.0", features = ["logs", "metrics", "trace"] }

implementations/rust/ockam/ockam_api/src/cli_state/cli_state.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ impl CliState {
118118
}
119119

120120
fn notify(&self, notification: Notification) {
121-
debug!("{:?}", notification.contents());
122121
let _ = self.notifications.send(notification);
123122
}
124123
}

implementations/rust/ockam/ockam_api/src/logs/logging_options.rs

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
use nu_ansi_term::{Color, Style};
12
use ockam_core::env::FromString;
23
use ockam_core::errcode::{Kind, Origin};
3-
use std::fmt::{Display, Formatter};
4+
use std::fmt::{Debug, Display, Formatter};
5+
use tracing_core::{Event, Level, Subscriber};
6+
use tracing_subscriber::fmt::format::Writer;
7+
use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields, FormattedFields};
8+
use tracing_subscriber::registry::LookupSpan;
49

510
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
611
pub enum LoggingEnabled {
@@ -85,12 +90,158 @@ impl FromString for LogFormat {
8590
}
8691
}
8792

88-
impl std::fmt::Display for LogFormat {
89-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93+
impl Display for LogFormat {
94+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
9095
match self {
9196
LogFormat::Default => write!(f, "default"),
9297
LogFormat::Pretty => write!(f, "pretty"),
9398
LogFormat::Json => write!(f, "json"),
9499
}
95100
}
96101
}
102+
103+
#[derive(Default)]
104+
pub struct OckamLogFormat {}
105+
106+
impl OckamLogFormat {
107+
pub fn new() -> Self {
108+
Self {}
109+
}
110+
111+
fn format_timestamp(&self, writer: &mut Writer<'_>) -> std::fmt::Result {
112+
let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.3f");
113+
if writer.has_ansi_escapes() {
114+
let style = Style::new().dimmed();
115+
write!(writer, "{}", style.prefix())?;
116+
write!(writer, "{}", now)?;
117+
write!(writer, "{} ", style.suffix())?;
118+
} else {
119+
write!(writer, "{}", now)?;
120+
writer.write_char(' ')?;
121+
}
122+
Ok(())
123+
}
124+
}
125+
126+
impl<S, N> FormatEvent<S, N> for OckamLogFormat
127+
where
128+
S: Subscriber + for<'a> LookupSpan<'a>,
129+
N: for<'a> FormatFields<'a> + 'static,
130+
{
131+
fn format_event(
132+
&self,
133+
ctx: &FmtContext<'_, S, N>,
134+
mut writer: Writer<'_>,
135+
event: &Event<'_>,
136+
) -> std::fmt::Result {
137+
let meta = event.metadata();
138+
let dimmed = if writer.has_ansi_escapes() {
139+
Style::new().dimmed()
140+
} else {
141+
Style::new()
142+
};
143+
let bold = if writer.has_ansi_escapes() {
144+
Style::new().bold()
145+
} else {
146+
Style::new()
147+
};
148+
149+
// Timestamp
150+
self.format_timestamp(&mut writer)?;
151+
152+
// Level
153+
let fmt_level = FmtLevel::new(meta.level(), writer.has_ansi_escapes());
154+
write!(writer, "{} ", fmt_level)?;
155+
156+
// Event
157+
ctx.format_fields(writer.by_ref(), event)?;
158+
writer.write_char(' ')?;
159+
160+
// Scope
161+
if let Some(scope) = ctx.event_scope() {
162+
let mut seen = false;
163+
164+
for span in scope.from_root() {
165+
write!(writer, "{}", bold.paint(span.metadata().name()))?;
166+
seen = true;
167+
168+
let ext = span.extensions();
169+
if let Some(fields) = &ext.get::<FormattedFields<N>>() {
170+
if !fields.is_empty() {
171+
write!(writer, "{}{}{}", bold.paint("{"), fields, bold.paint("}"))?;
172+
}
173+
}
174+
write!(writer, "{}", dimmed.paint(":"))?;
175+
}
176+
177+
if seen {
178+
writer.write_char(' ')?;
179+
}
180+
};
181+
182+
// Target
183+
write!(writer, "{} ", dimmed.paint(meta.target()))?;
184+
185+
// File and line
186+
let line_number = meta.line();
187+
if let Some(filename) = meta.file() {
188+
write!(
189+
writer,
190+
"{}{}{}",
191+
dimmed.paint(filename),
192+
dimmed.paint(":"),
193+
if line_number.is_some() { "" } else { " " }
194+
)?;
195+
}
196+
if let Some(line_number) = line_number {
197+
write!(
198+
writer,
199+
"{}{}{}",
200+
dimmed.prefix(),
201+
line_number,
202+
dimmed.suffix()
203+
)?;
204+
}
205+
206+
writeln!(writer)
207+
}
208+
}
209+
210+
struct FmtLevel<'a> {
211+
level: &'a Level,
212+
ansi: bool,
213+
}
214+
215+
impl<'a> FmtLevel<'a> {
216+
pub(crate) fn new(level: &'a Level, ansi: bool) -> Self {
217+
Self { level, ansi }
218+
}
219+
}
220+
221+
const TRACE_STR: &str = "TRACE";
222+
const DEBUG_STR: &str = "DEBUG";
223+
const INFO_STR: &str = " INFO";
224+
const WARN_STR: &str = " WARN";
225+
const ERROR_STR: &str = "ERROR";
226+
227+
impl Display for FmtLevel<'_> {
228+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
229+
if self.ansi {
230+
match *self.level {
231+
Level::TRACE => write!(f, "{}", Color::Purple.paint(TRACE_STR)),
232+
Level::DEBUG => write!(f, "{}", Color::Blue.paint(DEBUG_STR)),
233+
Level::INFO => write!(f, "{}", Color::Green.paint(INFO_STR)),
234+
Level::WARN => write!(f, "{}", Color::Yellow.paint(WARN_STR)),
235+
Level::ERROR => write!(f, "{}", Color::Red.paint(ERROR_STR)),
236+
}
237+
} else {
238+
match *self.level {
239+
Level::TRACE => f.pad(TRACE_STR),
240+
Level::DEBUG => f.pad(DEBUG_STR),
241+
Level::INFO => f.pad(INFO_STR),
242+
Level::WARN => f.pad(WARN_STR),
243+
Level::ERROR => f.pad(ERROR_STR),
244+
}
245+
}
246+
}
247+
}

implementations/rust/ockam/ockam_api/src/logs/setup.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use ockam_node::Executor;
3030
use crate::logs::tracing_guard::TracingGuard;
3131
use crate::logs::{
3232
ExportingConfiguration, GlobalErrorHandler, LoggingConfiguration, OckamLogExporter,
33+
OckamLogFormat,
3334
};
3435
use crate::logs::{LogFormat, OckamSpanExporter};
3536

@@ -109,7 +110,9 @@ impl LoggingTracing {
109110
let result = match logging_configuration.format() {
110111
LogFormat::Pretty => layers.with(appender.pretty()).try_init(),
111112
LogFormat::Json => layers.with(appender.json()).try_init(),
112-
LogFormat::Default => layers.with(appender).try_init(),
113+
LogFormat::Default => layers
114+
.with(appender.event_format(OckamLogFormat::new()))
115+
.try_init(),
113116
};
114117
result.expect("Failed to initialize tracing subscriber");
115118

@@ -130,7 +133,9 @@ impl LoggingTracing {
130133
let result = match logging_configuration.format() {
131134
LogFormat::Pretty => layers.with(appender.pretty()).try_init(),
132135
LogFormat::Json => layers.with(appender.json()).try_init(),
133-
LogFormat::Default => layers.with(appender).try_init(),
136+
LogFormat::Default => layers
137+
.with(appender.event_format(OckamLogFormat::new()))
138+
.try_init(),
134139
};
135140
result.expect("Failed to initialize tracing subscriber");
136141
};
@@ -230,7 +235,7 @@ fn create_opentelemetry_tracing_layer<
230235
exporting_configuration: &ExportingConfiguration,
231236
span_exporter: S,
232237
) -> (
233-
OpenTelemetryLayer<R, sdk::trace::Tracer>,
238+
OpenTelemetryLayer<R, opentelemetry_sdk::trace::Tracer>,
234239
opentelemetry_sdk::trace::TracerProvider,
235240
) {
236241
let app = app_name.to_string();
@@ -243,7 +248,8 @@ fn create_opentelemetry_tracing_layer<
243248
let is_ockam_developer = exporting_configuration.is_ockam_developer();
244249
let span_export_cutoff = exporting_configuration.span_export_cutoff();
245250
Executor::execute_future(async move {
246-
let trace_config = sdk::trace::Config::default().with_resource(make_resource(app));
251+
let trace_config =
252+
opentelemetry_sdk::trace::Config::default().with_resource(make_resource(app));
247253
let (tracer, tracer_provider) = create_tracer(
248254
trace_config,
249255
batch_config,
@@ -410,11 +416,14 @@ fn set_global_error_handler(logging_configuration: &LoggingConfiguration) {
410416

411417
/// Create a Tracer using the provided span exporter
412418
fn create_tracer<S: SpanExporter + 'static>(
413-
trace_config: sdk::trace::Config,
419+
trace_config: opentelemetry_sdk::trace::Config,
414420
batch_config: BatchConfig,
415421
exporter: S,
416-
) -> (sdk::trace::Tracer, opentelemetry_sdk::trace::TracerProvider) {
417-
let span_processor = BatchSpanProcessor::builder(exporter, sdk::runtime::Tokio)
422+
) -> (
423+
opentelemetry_sdk::trace::Tracer,
424+
opentelemetry_sdk::trace::TracerProvider,
425+
) {
426+
let span_processor = BatchSpanProcessor::builder(exporter, opentelemetry_sdk::runtime::Tokio)
418427
.with_batch_config(batch_config)
419428
.build();
420429
let provider = opentelemetry_sdk::trace::TracerProvider::builder()

implementations/rust/ockam/ockam_api/tests/logging_tracing.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use opentelemetry_sdk as sdk;
1010
use sdk::testing::logs::*;
1111
use sdk::testing::trace::*;
1212

13+
use opentelemetry_sdk::testing::logs::InMemoryLogsExporter;
14+
use opentelemetry_sdk::testing::trace::InMemorySpanExporter;
1315
use std::fs;
14-
1516
use tempfile::NamedTempFile;
1617

1718
use ockam_api::cli_state::{random_name, CliStateMode};
@@ -81,12 +82,12 @@ fn test_log_and_traces() {
8182
if file_path.to_string_lossy().contains("stdout") {
8283
let contents = fs::read_to_string(file_path).unwrap();
8384
assert!(
84-
contents.contains("INFO logging_tracing: inside span"),
85+
contents.contains("INFO inside span logging_tracing"),
8586
"{:?}",
8687
contents
8788
);
8889
assert!(
89-
contents.contains("ERROR logging_tracing: something went wrong!"),
90+
contents.contains("ERROR something went wrong! logging_tracing"),
9091
"{:?}",
9192
contents
9293
);

implementations/rust/ockam/ockam_command/src/command_global_opts.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ impl CommandGlobalOpts {
185185
debug!("Arguments: {}", arguments.join(" "));
186186
debug!("Global arguments: {:#?}", &global_args);
187187
debug!("Command: {:#?}", &cmd);
188-
debug!("Version: {}", Version::new());
188+
debug!("Version: {}", Version::new().no_color());
189189

190190
info!("Tracing initialized");
191191
debug!("{:#?}", logging_configuration);

implementations/rust/ockam/ockam_command/src/node/create/foreground.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,17 @@ impl CreateCommand {
5353

5454
// Set node_name so that node can isolate its data in the storage from other nodes
5555
self.get_or_create_identity(&opts, &self.identity).await?;
56-
let _notification_handler = NotificationHandler::start(&opts.state, opts.terminal.clone());
56+
let _notification_handler = if self.foreground_args.child_process {
57+
// If enabled, the user's terminal will receive notifications
58+
// from the node after the command exited.
59+
None
60+
} else {
61+
// Enable the notifications only on explicit foreground nodes.
62+
Some(NotificationHandler::start(
63+
&opts.state,
64+
opts.terminal.clone(),
65+
))
66+
};
5767
let node_info = opts
5868
.state
5969
.start_node_with_optional_values(

0 commit comments

Comments
 (0)