Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

log plugin compat #158

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion devtools/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,13 @@ where

fn on_event(&self, event: &tracing_core::Event<'_>, ctx: Context<'_, S>) {
let at = Instant::now();
let metadata = event.metadata();

self.send_event(&self.shared.dropped_log_events, || {
let metadata = event.metadata();
Event::Metadata(metadata)
});

self.send_event(&self.shared.dropped_log_events, || {
let mut visitor = EventVisitor::new(metadata as *const _ as u64);
event.record(&mut visitor);
let (message, fields) = visitor.result();
Expand Down
43 changes: 42 additions & 1 deletion devtools/src/tauri_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
use crate::aggregator::Aggregator;
use crate::server::Server;
use crate::Command;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::thread;
use std::time::Duration;
use tauri::Runtime;
use tokio::sync::mpsc;

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Key {
level: u16,
file: Option<String>,
line: Option<u32>,
}

#[allow(clippy::needless_pass_by_value)]
#[tauri::command]
fn log(
level: u16,
message: &str,
location: Option<&str>,
file: Option<&str>,
line: Option<u32>,
_key_values: Option<HashMap<String, String>>,
) {
match level {
1 => {
tracing::trace!(target: "log", message, log.target = "webview", log.module_path = location, log.file = file, log.line = line);
}
2 => {
tracing::debug!(target: "log", message, log.target = "webview", log.module_path = location, log.file = file, log.line = line);
}
3 => {
tracing::info!(target: "log", message, log.target = "webview", log.module_path = location, log.file = file, log.line = line);
}
4 => {
tracing::warn!(target: "log", message, log.target = "webview", log.module_path = location, log.file = file, log.line = line);
}
5 => {
tracing::error!(target: "log", message, log.target = "webview", log.module_path = location, log.file = file, log.line = line);
}
_ => {}
}
}

pub(crate) fn init<R: Runtime>(
addr: SocketAddr,
publish_interval: Duration,
aggregator: Aggregator,
cmd_tx: mpsc::Sender<Command>,
) -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("probe")
// we pretend to be the log plugin so we can intercept the commands
// this plugin is incompatible with the log plugin anyway
tauri::plugin::Builder::new("log")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like this.. I feel if someone is using devtools, they shouldn't need the log plugin. We should document that instead (e.g. use the log plugin on production, devtools on development).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that is the idea, but as stated in the issue #150 devtools misses out the one feature that makes tauri-plugin-log great: the ability to emit logs from both Rust and JS to the same place.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think about making the log plugin not crash on init? so you can keep using its APIs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that could work actually

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gonna be difficult though, bc we have to init the log plugin in the setup callback not the builder function

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does it matter? as long as we tell people to enable devtools first and both register at the same place..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean like this?

tauri::Builder::default()
    .plugin(devtools::init())
    .plugin(tauri_plugin_log::init())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah (plus where we actually register the log plugin)

.invoke_handler(tauri::generate_handler![log])
.setup(move |app_handle| {
let server = Server::new(cmd_tx, app_handle.clone());

Expand Down
47 changes: 45 additions & 2 deletions devtools/src/visitors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,54 @@ impl Visit for FieldVisitor {
}

impl Visit for EventVisitor {
/// Visit a value that implements `Debug`.
/// Visit a double-precision floating point value.
fn record_f64(&mut self, field: &tracing_core::Field, value: f64) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
"message" if self.message.is_none() => self.message = Some(value.to_string()),
_ => self.field_visitor.record_f64(field, value),
}
}

/// Visit a signed 64-bit integer value.
fn record_i64(&mut self, field: &tracing_core::Field, value: i64) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
"message" if self.message.is_none() => self.message = Some(value.to_string()),
_ => self.field_visitor.record_i64(field, value),
}
}

/// Visit an unsigned 64-bit integer value.
fn record_u64(&mut self, field: &tracing_core::Field, value: u64) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
"message" if self.message.is_none() => self.message = Some(value.to_string()),
_ => self.field_visitor.record_u64(field, value),
}
}

/// Visit a boolean value.
fn record_bool(&mut self, field: &tracing_core::Field, value: bool) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
"message" if self.message.is_none() => self.message = Some(value.to_string()),
_ => self.field_visitor.record_bool(field, value),
}
}

/// Visit a string value.
fn record_str(&mut self, field: &tracing_core::Field, value: &str) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
"message" if self.message.is_none() => self.message = Some(value.to_string()),
_ => self.field_visitor.record_str(field, value),
}
}

fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn Debug) {
match field.name() {
// skip fields that are `log` metadata that have already been handled
name if name.starts_with("log.") => (),
"message" if self.message.is_none() => self.message = Some(format!("{value:?}")),
_ => self.field_visitor.record_debug(field, value),
}
Expand Down
54 changes: 50 additions & 4 deletions web-client/src/views/dashboard/console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import { LogLevelFilter } from "~/components/console/log-level-filter";
import { NoLogs } from "~/components/console/no-logs";
import { getFileNameFromPath } from "~/lib/console/get-file-name-from-path";
import { processFieldValue } from "~/lib/span/process-field-value.ts";

export default function Console() {
const { monitorData } = useMonitor();
Expand Down Expand Up @@ -69,6 +70,43 @@
const timeDate = timestampToDate(at);
const levelStyle = getLevelClasses(metadata?.level);

let target = metadata?.target;
let location = metadata?.location;
if (target === "log") {
const field = logEvent.fields.find(
(field) => field.name === "log.target"
);
if (field) {
target = processFieldValue(field.value);
}
}
if (target === "webview") {
const file = logEvent.fields.find(
(field) => field.name === "log.file"
);
const line = logEvent.fields.find(
(field) => field.name === "log.line"
);
const mod = logEvent.fields.find(
(field) => field.name === "log.module_path"
);

let mod_file, mod_line;
if (mod) {
const str = processFieldValue(mod.value).replace("log@", "");
const [http, url, port, line, col] = str.split(":");

mod_file = http + url + port;
mod_line = parseInt(line);
console.log(http, url, port, line, col);
}

location = {
file: file ? processFieldValue(file.value) : mod_file,
line: line ? parseInt(processFieldValue(line.value)) : mod_line,
};
}

return (
<li
class={clsx(
Expand All @@ -89,11 +127,19 @@
</Show>
<span>{message}</span>
<span class="ml-auto flex gap-2 items-center text-xs">
<Show when={metadata?.target}>
<span class="text-gray-600">{metadata!.target}</span>
<Show when={target}>
<span class="text-gray-600">{target}</span>
</Show>
<Show when={location?.file}>
{getFileNameFromPath(location!.file!)}:{location!.line}

Check warning on line 134 in web-client/src/views/dashboard/console.tsx

View workflow job for this annotation

GitHub Actions / test

Forbidden non-null assertion
</Show>
<Show when={metadata?.location?.file}>
{getFileNameFromPath(metadata!.location!.file!)}:
<Show
when={
!metadata?.location?.file &&
metadata?.location?.modulePath
}
>
{getFileNameFromPath(metadata!.location!.modulePath!)}:
{metadata!.location!.line}
</Show>
</span>
Expand Down
Loading