diff --git a/backend/.sqlx/query-5c54f145e94dac117de02a94adf207684c52d8571b3507f4877c2cc151ff18b9.json b/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json similarity index 59% rename from backend/.sqlx/query-5c54f145e94dac117de02a94adf207684c52d8571b3507f4877c2cc151ff18b9.json rename to backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json index 642f4a9592..8728e35a0c 100644 --- a/backend/.sqlx/query-5c54f145e94dac117de02a94adf207684c52d8571b3507f4877c2cc151ff18b9.json +++ b/backend/.sqlx/query-33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7)", + "query": "INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", "describe": { "columns": [], "parameters": { @@ -11,10 +11,11 @@ "Timestamp", "Varchar", "Int8", - "Int8" + "Int8", + "Bool" ] }, "nullable": [] }, - "hash": "5c54f145e94dac117de02a94adf207684c52d8571b3507f4877c2cc151ff18b9" + "hash": "33c1793e55b1127d88d2509aadd0eb04e042463200f237b4c2cb176612fa16fe" } diff --git a/backend/migrations/20240831202016_add_json_fmt_to_log_file.down.sql b/backend/migrations/20240831202016_add_json_fmt_to_log_file.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20240831202016_add_json_fmt_to_log_file.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20240831202016_add_json_fmt_to_log_file.up.sql b/backend/migrations/20240831202016_add_json_fmt_to_log_file.up.sql new file mode 100644 index 0000000000..df50f2bc9a --- /dev/null +++ b/backend/migrations/20240831202016_add_json_fmt_to_log_file.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE log_file ADD COLUMN IF NOT EXISTS json_fmt boolean DEFAULT false; \ No newline at end of file diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 58d6c3be82..2c204b38a9 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -42,6 +42,7 @@ use windmill_common::{ jobs::QueuedJob, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_server_config, + tracing_init::JSON_FMT, users::truncate_token, utils::{now_from_db, rd_string, report_critical_error, Mode}, worker::{ @@ -500,8 +501,8 @@ async fn send_log_file_to_object_store( let (ok_lines, err_lines) = read_log_counters(ts_str); - if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7)", - hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64) + if let Err(e) = sqlx::query!("INSERT INTO log_file (hostname, mode, worker_group, log_ts, file_path, ok_lines, err_lines, json_fmt) VALUES ($1, $2::text::LOG_MODE, $3, $4, $5, $6, $7, $8)", + hostname, mode.to_string(), worker_group.clone(), ts, highest_file, ok_lines as i64, err_lines as i64, *JSON_FMT) .execute(db) .await { tracing::error!("Error inserting log file: {:?}", e); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 7bf1abd9af..7252c6f873 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -8642,11 +8642,14 @@ paths: type: integer err_lines: type: integer + json_fmt: + type: boolean required: - hostname - mode - log_ts - file_path + - json_fmt /service_logs/get_log_file/{path}: get: diff --git a/backend/windmill-api/src/service_logs.rs b/backend/windmill-api/src/service_logs.rs index f563f9be71..eb5b2f44f2 100644 --- a/backend/windmill-api/src/service_logs.rs +++ b/backend/windmill-api/src/service_logs.rs @@ -43,6 +43,7 @@ pub struct LogFile { pub file_path: String, pub ok_lines: Option, pub err_lines: Option, + pub json_fmt: bool, } async fn list_files( ApiAuthed { email, .. }: ApiAuthed, @@ -62,6 +63,7 @@ async fn list_files( "file_path", "ok_lines", "err_lines", + "json_fmt", ]) .order_by("log_ts", true) .offset(offset) diff --git a/backend/windmill-common/src/tracing_init.rs b/backend/windmill-common/src/tracing_init.rs index d60f98a496..13b345e36e 100644 --- a/backend/windmill-common/src/tracing_init.rs +++ b/backend/windmill-common/src/tracing_init.rs @@ -26,15 +26,16 @@ fn compact_layer() -> Layer WorkerGuard { let style = std::env::var("RUST_LOG_STYLE").unwrap_or_else(|_| "auto".into()); - let json_fmt = std::env::var("JSON_FMT") - .map(|x| x == "true") - .unwrap_or(false); if std::env::var("RUST_LOG").is_ok_and(|x| x == "debug" || x == "info") { std::env::set_var( @@ -71,13 +72,14 @@ pub fn initialize_tracing(hostname: &str) -> WorkerGuard { ts_base.with(layer) }; - match json_fmt { + match *JSON_FMT { true => ts_base .with( json_layer() .with_writer(stdout_and_log_file_writer) .flatten_event(true), ) + .with(CountingLayer::new()) .init(), false => ts_base .with( diff --git a/frontend/src/lib/components/ServiceLogsInner.svelte b/frontend/src/lib/components/ServiceLogsInner.svelte index f4350961ba..a86959de3e 100644 --- a/frontend/src/lib/components/ServiceLogsInner.svelte +++ b/frontend/src/lib/components/ServiceLogsInner.svelte @@ -9,6 +9,7 @@ import { sendUserToast } from '$lib/toast' import { onDestroy } from 'svelte' import { Loader2 } from 'lucide-svelte' + import { truncateRev } from '$lib/utils' let minTs: undefined | string = undefined let maxTs: undefined | string = undefined @@ -29,6 +30,7 @@ file_path: string ok_lines: number err_lines: number + json_fmt: boolean } type ByHostname = Record @@ -98,7 +100,8 @@ ts: ts, file_path: log.file_path, ok_lines: log.ok_lines ?? 1, - err_lines: log.err_lines ?? 0 + err_lines: log.err_lines ?? 0, + json_fmt: log.json_fmt }) if ( log.ok_lines != undefined && @@ -213,6 +216,50 @@ onDestroy(() => { timeout && clearTimeout(timeout) }) + + function processLogWithJsonFmt(log: string | undefined, jsonFmt: boolean): string { + if (!log) { + return '' + } + if (!jsonFmt) { + return log + } + try { + let res = '' + log.split('\n').forEach((line) => { + if (line.startsWith('{') && line.endsWith('}')) { + let obj = JSON.parse(line) + if (typeof obj == 'object') { + let nl = '' + if (obj['timestamp']) { + nl += obj['timestamp'] + ' ' + } + if (obj['level']) { + nl += obj['level'] + ' ' + } + if (obj['message']) { + nl += obj['message'] + ' ' + } + delete obj['timestamp'] + delete obj['level'] + delete obj['message'] + Object.keys(obj).forEach((key) => { + nl += + key + + '=' + + (typeof obj[key] == 'object' ? JSON.stringify(obj[key]) : obj[key]) + + ' ' + }) + res += nl + '\n' + } + } + }) + + return res + } catch (e) { + return log + } + }
@@ -346,7 +393,11 @@ scrollToBottom() }} > -
{hn}
+
{truncateRev(hn, 8)}
{#each files as file} {@const okHeight = 100.0 * ((file.ok_lines * 1.0) / (max_lines ?? 1))} @@ -421,7 +472,10 @@ noMaxH isLoading={false} tag={undefined} - content={logsContent[file.file_path].content} + content={processLogWithJsonFmt( + logsContent[file.file_path].content, + file.json_fmt + )} />
{:else}