mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-30 14:25:22 -05:00
improve insturmentation / serror
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -2896,9 +2896,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "resolver_api"
|
name = "resolver_api"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9245971c55ef05018019a1da642fce51439df2b4336369144a7bb866e91f79cf"
|
checksum = "43981e6bc9a85f1072ffd38bcaca5823e3cd7f24bb675fa5e79736a318f1f998"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3251,9 +3251,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serror"
|
name = "serror"
|
||||||
version = "0.1.9"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d3df9a2b74d806ecbe70d5156f436edca357e458b8d970ce88cab324a599190"
|
checksum = "ac699ec20dda9d3630e499777c7822d9811beafbedf3b6305586b7338eb5a50e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum 0.7.4",
|
"axum 0.7.4",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ logger = { path = "lib/logger" }
|
|||||||
|
|
||||||
# MOGH
|
# MOGH
|
||||||
run_command = { version = "0.0.6", features = ["async_tokio"] }
|
run_command = { version = "0.0.6", features = ["async_tokio"] }
|
||||||
serror = { version = "0.1.9", features = ["axum"] }
|
serror = { version = "0.1.10", features = ["axum"] }
|
||||||
slack = { version = "0.1.0", package = "slack_client_rs" }
|
slack = { version = "0.1.0", package = "slack_client_rs" }
|
||||||
derive_default_builder = "0.1.8"
|
derive_default_builder = "0.1.8"
|
||||||
derive_empty_traits = "0.1.0"
|
derive_empty_traits = "0.1.0"
|
||||||
@@ -27,7 +27,7 @@ async_timing_util = "0.1.14"
|
|||||||
partial_derive2 = "0.2.2"
|
partial_derive2 = "0.2.2"
|
||||||
derive_variants = "0.1.3"
|
derive_variants = "0.1.3"
|
||||||
mongo_indexed = "0.2.2"
|
mongo_indexed = "0.2.2"
|
||||||
resolver_api = "0.1.8"
|
resolver_api = "0.1.9"
|
||||||
parse_csl = "0.1.0"
|
parse_csl = "0.1.0"
|
||||||
mungos = "0.5.4"
|
mungos = "0.5.4"
|
||||||
svi = "0.1.4"
|
svi = "0.1.4"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::{sync::OnceLock, time::Instant};
|
use std::{sync::OnceLock, time::Instant};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum::{http::HeaderMap, routing::post, Json, Router};
|
use axum::{http::HeaderMap, routing::post, Json, Router};
|
||||||
use axum_extra::{headers::ContentType, TypedHeader};
|
use axum_extra::{headers::ContentType, TypedHeader};
|
||||||
@@ -58,11 +59,15 @@ async fn handler(
|
|||||||
let timer = Instant::now();
|
let timer = Instant::now();
|
||||||
let req_id = Uuid::new_v4();
|
let req_id = Uuid::new_v4();
|
||||||
debug!("/auth request {req_id} | METHOD: {}", request.req_type());
|
debug!("/auth request {req_id} | METHOD: {}", request.req_type());
|
||||||
let res = State.resolve_request(request, headers).await;
|
let res = State.resolve_request(request, headers).await.map_err(
|
||||||
if let Err(resolver_api::Error::Serialization(e)) = &res {
|
|e| match e {
|
||||||
debug!("/auth request {req_id} | serialization error: {e:?}");
|
resolver_api::Error::Serialization(e) => {
|
||||||
}
|
anyhow!("{e:?}").context("response serialization error")
|
||||||
if let Err(resolver_api::Error::Inner(e)) = &res {
|
}
|
||||||
|
resolver_api::Error::Inner(e) => e,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if let Err(e) = &res {
|
||||||
debug!("/auth request {req_id} | error: {e:#}");
|
debug!("/auth request {req_id} | error: {e:#}");
|
||||||
}
|
}
|
||||||
let elapsed = timer.elapsed();
|
let elapsed = timer.elapsed();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use axum::{middleware, routing::post, Extension, Json, Router};
|
use axum::{middleware, routing::post, Extension, Json, Router};
|
||||||
use axum_extra::{headers::ContentType, TypedHeader};
|
use axum_extra::{headers::ContentType, TypedHeader};
|
||||||
use monitor_client::{api::execute::*, entities::user::User};
|
use monitor_client::{api::execute::*, entities::user::User};
|
||||||
@@ -54,38 +54,52 @@ pub fn router() -> Router {
|
|||||||
.layer(middleware::from_fn(auth_request))
|
.layer(middleware::from_fn(auth_request))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(name = "ExecuteHandler", skip(user))]
|
|
||||||
async fn handler(
|
async fn handler(
|
||||||
Extension(user): Extension<User>,
|
Extension(user): Extension<User>,
|
||||||
Json(request): Json<ExecuteRequest>,
|
Json(request): Json<ExecuteRequest>,
|
||||||
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
||||||
let timer = Instant::now();
|
|
||||||
let req_id = Uuid::new_v4();
|
let req_id = Uuid::new_v4();
|
||||||
info!(
|
|
||||||
"/execute request {req_id} | user: {} ({})",
|
let res = tokio::spawn(task(req_id, request, user))
|
||||||
user.username, user.id
|
.await
|
||||||
);
|
.context("failure in spawned execute task");
|
||||||
let res = tokio::spawn(async move {
|
|
||||||
State.resolve_request(request, user).await
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.context("failure in spawned execute task");
|
|
||||||
|
|
||||||
if let Err(e) = &res {
|
if let Err(e) = &res {
|
||||||
warn!("/execute request {req_id} spawn error: {e:#}",);
|
warn!("/execute request {req_id} spawn error: {e:#}",);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = res?;
|
AppResult::Ok((TypedHeader(ContentType::json()), res??))
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(resolver_api::Error::Serialization(e)) = &res {
|
#[instrument(name = "ExecuteTask")]
|
||||||
warn!("/execute request {req_id} serialization error: {e:?}");
|
async fn task(
|
||||||
}
|
req_id: Uuid,
|
||||||
if let Err(resolver_api::Error::Inner(e)) = &res {
|
request: ExecuteRequest,
|
||||||
|
user: User,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
info!(
|
||||||
|
"/execute request {req_id} | user: {} ({})",
|
||||||
|
user.username, user.id
|
||||||
|
);
|
||||||
|
let timer = Instant::now();
|
||||||
|
|
||||||
|
let res =
|
||||||
|
State
|
||||||
|
.resolve_request(request, user)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
resolver_api::Error::Serialization(e) => {
|
||||||
|
anyhow!("{e:?}").context("response serialization error")
|
||||||
|
}
|
||||||
|
resolver_api::Error::Inner(e) => e,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = &res {
|
||||||
warn!("/execute request {req_id} error: {e:#}");
|
warn!("/execute request {req_id} error: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = timer.elapsed();
|
let elapsed = timer.elapsed();
|
||||||
info!("/execute request {req_id} | resolve time: {elapsed:?}");
|
info!("/execute request {req_id} | resolve time: {elapsed:?}");
|
||||||
|
|
||||||
AppResult::Ok((TypedHeader(ContentType::json()), res?))
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum::{middleware, routing::post, Extension, Json, Router};
|
use axum::{middleware, routing::post, Extension, Json, Router};
|
||||||
use axum_extra::{headers::ContentType, TypedHeader};
|
use axum_extra::{headers::ContentType, TypedHeader};
|
||||||
@@ -150,11 +151,17 @@ async fn handler(
|
|||||||
"/read request {req_id} | user: {} ({})",
|
"/read request {req_id} | user: {} ({})",
|
||||||
user.username, user.id
|
user.username, user.id
|
||||||
);
|
);
|
||||||
let res = State.resolve_request(request, user).await;
|
let res =
|
||||||
if let Err(resolver_api::Error::Serialization(e)) = &res {
|
State
|
||||||
warn!("/read request {req_id} serialization error: {e:?}");
|
.resolve_request(request, user)
|
||||||
}
|
.await
|
||||||
if let Err(resolver_api::Error::Inner(e)) = &res {
|
.map_err(|e| match e {
|
||||||
|
resolver_api::Error::Serialization(e) => {
|
||||||
|
anyhow!("{e:?}").context("response serialization error")
|
||||||
|
}
|
||||||
|
resolver_api::Error::Inner(e) => e,
|
||||||
|
});
|
||||||
|
if let Err(e) = &res {
|
||||||
warn!("/read request {req_id} error: {e:#}");
|
warn!("/read request {req_id} error: {e:#}");
|
||||||
}
|
}
|
||||||
let elapsed = timer.elapsed();
|
let elapsed = timer.elapsed();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use axum::{middleware, routing::post, Extension, Json, Router};
|
use axum::{middleware, routing::post, Extension, Json, Router};
|
||||||
use axum_extra::{headers::ContentType, TypedHeader};
|
use axum_extra::{headers::ContentType, TypedHeader};
|
||||||
use monitor_client::{api::write::*, entities::user::User};
|
use monitor_client::{api::write::*, entities::user::User};
|
||||||
@@ -115,33 +115,49 @@ async fn handler(
|
|||||||
Extension(user): Extension<User>,
|
Extension(user): Extension<User>,
|
||||||
Json(request): Json<WriteRequest>,
|
Json(request): Json<WriteRequest>,
|
||||||
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
||||||
let timer = Instant::now();
|
|
||||||
let req_id = Uuid::new_v4();
|
let req_id = Uuid::new_v4();
|
||||||
info!(
|
|
||||||
"/write request {req_id} | user: {} ({})",
|
let res = tokio::spawn(task(req_id, request, user))
|
||||||
user.username, user.id
|
.await
|
||||||
);
|
.context("failure in spawned task");
|
||||||
let res = tokio::spawn(async move {
|
|
||||||
State.resolve_request(request, user).await
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.context("failure in spawned task");
|
|
||||||
|
|
||||||
if let Err(e) = &res {
|
if let Err(e) = &res {
|
||||||
warn!("/write request {req_id} spawn error: {e:#}");
|
warn!("/write request {req_id} spawn error: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = res?;
|
AppResult::Ok((TypedHeader(ContentType::json()), res??))
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(resolver_api::Error::Serialization(e)) = &res {
|
#[instrument(name = "WriteTask")]
|
||||||
warn!("/write request {req_id} serialization error: {e:?}");
|
async fn task(
|
||||||
}
|
req_id: Uuid,
|
||||||
if let Err(resolver_api::Error::Inner(e)) = &res {
|
request: WriteRequest,
|
||||||
|
user: User,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
info!(
|
||||||
|
"/write request {req_id} | user: {} ({})",
|
||||||
|
user.username, user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
let timer = Instant::now();
|
||||||
|
|
||||||
|
let res =
|
||||||
|
State
|
||||||
|
.resolve_request(request, user)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
resolver_api::Error::Serialization(e) => {
|
||||||
|
anyhow!("{e:?}").context("response serialization error")
|
||||||
|
}
|
||||||
|
resolver_api::Error::Inner(e) => e,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = &res {
|
||||||
warn!("/write request {req_id} error: {e:#}");
|
warn!("/write request {req_id} error: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = timer.elapsed();
|
let elapsed = timer.elapsed();
|
||||||
info!("/write request {req_id} | resolve time: {elapsed:?}");
|
info!("/write request {req_id} | resolve time: {elapsed:?}");
|
||||||
|
|
||||||
AppResult::Ok((TypedHeader(ContentType::json()), res?))
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
use axum_extra::{headers::ContentType, TypedHeader};
|
use axum_extra::{headers::ContentType, TypedHeader};
|
||||||
use resolver_api::Resolver;
|
use resolver_api::Resolver;
|
||||||
@@ -8,33 +9,47 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::State;
|
use crate::State;
|
||||||
|
|
||||||
#[instrument(name = "PeripheryHandler")]
|
|
||||||
pub async fn handler(
|
pub async fn handler(
|
||||||
Json(request): Json<crate::api::PeripheryRequest>,
|
Json(request): Json<crate::api::PeripheryRequest>,
|
||||||
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
) -> AppResult<(TypedHeader<ContentType>, String)> {
|
||||||
let timer = Instant::now();
|
|
||||||
let req_id = Uuid::new_v4();
|
let req_id = Uuid::new_v4();
|
||||||
info!("request {req_id} | {request:?}");
|
|
||||||
let res =
|
|
||||||
tokio::spawn(
|
|
||||||
async move { State.resolve_request(request, ()).await },
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let elapsed = timer.elapsed();
|
let res = tokio::spawn(task(req_id, request))
|
||||||
info!("request {req_id} | resolve time: {elapsed:?}");
|
.await
|
||||||
|
.context("task handler spawn error");
|
||||||
|
|
||||||
if let Err(e) = &res {
|
if let Err(e) = &res {
|
||||||
warn!("request {req_id} spawn error: {e:#}");
|
warn!("request {req_id} spawn error: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = res?;
|
AppResult::Ok((TypedHeader(ContentType::json()), res??))
|
||||||
if let Err(resolver_api::Error::Serialization(e)) = &res {
|
}
|
||||||
warn!("request {req_id} serialization error: {e:?}");
|
|
||||||
}
|
#[instrument(name = "PeripheryTask")]
|
||||||
if let Err(resolver_api::Error::Inner(e)) = &res {
|
async fn task(
|
||||||
|
req_id: Uuid,
|
||||||
|
request: crate::api::PeripheryRequest,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
info!("request {req_id} | {request:?}");
|
||||||
|
let timer = Instant::now();
|
||||||
|
|
||||||
|
let res =
|
||||||
|
State
|
||||||
|
.resolve_request(request, ())
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
resolver_api::Error::Serialization(e) => {
|
||||||
|
anyhow!("{e:?}").context("response serialization error")
|
||||||
|
}
|
||||||
|
resolver_api::Error::Inner(e) => e,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = &res {
|
||||||
warn!("request {req_id} error: {e:#}");
|
warn!("request {req_id} error: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
AppResult::Ok((TypedHeader(ContentType::json()), res?))
|
let elapsed = timer.elapsed();
|
||||||
|
info!("request {req_id} | resolve time: {elapsed:?}");
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1027,6 +1027,9 @@ export type AlertData =
|
|||||||
server_name: string;
|
server_name: string;
|
||||||
from: DockerContainerState;
|
from: DockerContainerState;
|
||||||
to: DockerContainerState;
|
to: DockerContainerState;
|
||||||
|
}}
|
||||||
|
| { type: "AwsBuilderTerminationFailed", data: {
|
||||||
|
instance_id: string;
|
||||||
}}
|
}}
|
||||||
| { type: "None", data: {
|
| { type: "None", data: {
|
||||||
}};
|
}};
|
||||||
@@ -1166,7 +1169,7 @@ export interface GetLog {
|
|||||||
export interface SearchLog {
|
export interface SearchLog {
|
||||||
/** Id or name */
|
/** Id or name */
|
||||||
deployment: string;
|
deployment: string;
|
||||||
search: string;
|
terms: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GetDeployedVersion {
|
export interface GetDeployedVersion {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Section } from "@components/layouts";
|
import { Section } from "@components/layouts";
|
||||||
import { useRead } from "@lib/hooks";
|
import { useRead } from "@lib/hooks";
|
||||||
import { Types } from "@monitor/client";
|
import { Types } from "@monitor/client";
|
||||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@ui/tabs";
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@ui/tabs";
|
||||||
@@ -8,8 +8,10 @@ import {
|
|||||||
AlertOctagon,
|
AlertOctagon,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
|
Search,
|
||||||
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState } from "react";
|
import { createRef, useState } from "react";
|
||||||
import { useDeployment } from ".";
|
import { useDeployment } from ".";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -21,6 +23,7 @@ import {
|
|||||||
} from "@ui/select";
|
} from "@ui/select";
|
||||||
import sanitizeHtml from "sanitize-html";
|
import sanitizeHtml from "sanitize-html";
|
||||||
import Convert from "ansi-to-html";
|
import Convert from "ansi-to-html";
|
||||||
|
import { Input } from "@ui/input";
|
||||||
|
|
||||||
export const DeploymentLogs = ({ id }: { id: string }) => {
|
export const DeploymentLogs = ({ id }: { id: string }) => {
|
||||||
const state = useDeployment(id)?.info.state;
|
const state = useDeployment(id)?.info.state;
|
||||||
@@ -36,11 +39,22 @@ export const DeploymentLogs = ({ id }: { id: string }) => {
|
|||||||
|
|
||||||
const DeploymentLogsInner = ({ id }: { id: string }) => {
|
const DeploymentLogsInner = ({ id }: { id: string }) => {
|
||||||
const [tail, set] = useState("100");
|
const [tail, set] = useState("100");
|
||||||
const { data: logs, refetch } = useRead(
|
const searchRef = createRef<HTMLInputElement>();
|
||||||
"GetLog",
|
const [search, setSearch] = useState("");
|
||||||
{ deployment: id, tail: Number(tail) },
|
|
||||||
{ refetchInterval: 30000 }
|
const updateSearch = () =>
|
||||||
);
|
searchRef.current && setSearch(searchRef.current.value);
|
||||||
|
const clearSearch = () => {
|
||||||
|
if (searchRef.current) {
|
||||||
|
searchRef.current.value = "";
|
||||||
|
}
|
||||||
|
setSearch("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const { Log, refetch, stderr } = search
|
||||||
|
? SearchLogs(id, search)
|
||||||
|
: NoSearchLogs(id, tail);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="stdout">
|
<Tabs defaultValue="stdout">
|
||||||
<Section
|
<Section
|
||||||
@@ -48,60 +62,121 @@ const DeploymentLogsInner = ({ id }: { id: string }) => {
|
|||||||
icon={<TerminalSquare className="w-4 h-4" />}
|
icon={<TerminalSquare className="w-4 h-4" />}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<TabsList className="w-fit place-self-end">
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
ref={searchRef}
|
||||||
|
placeholder="Search Logs"
|
||||||
|
onBlur={updateSearch}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") updateSearch();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={clearSearch}
|
||||||
|
className="absolute right-0 top-1/2 -translate-y-1/2"
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button variant="outline" size="icon" onClick={updateSearch}>
|
||||||
|
<Search className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<TabsList>
|
||||||
<TabsTrigger value="stdout" onClick={to_bottom("stdout")}>
|
<TabsTrigger value="stdout" onClick={to_bottom("stdout")}>
|
||||||
stdout
|
stdout
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="stderr" onClick={to_bottom("stderr")}>
|
<TabsTrigger value="stderr" onClick={to_bottom("stderr")}>
|
||||||
stderr
|
stderr
|
||||||
{logs?.stderr && (
|
{stderr && (
|
||||||
<AlertOctagon className="w-4 h-4 ml-2 stroke-red-500" />
|
<AlertOctagon className="w-4 h-4 ml-2 stroke-red-500" />
|
||||||
)}
|
)}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<Button variant="secondary" onClick={() => refetch()}>
|
<Button variant="secondary" size="icon" onClick={() => refetch()}>
|
||||||
<RefreshCw className="w-4 h-4" />
|
<RefreshCw className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<TailLengthSelector selected={tail} onSelect={set} />
|
<TailLengthSelector
|
||||||
|
selected={tail}
|
||||||
|
onSelect={set}
|
||||||
|
disabled={search.length > 0}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{["stdout", "stderr"].map((t) => {
|
{Log}
|
||||||
const log = logs?.[t as keyof typeof logs] as string | undefined;
|
|
||||||
return (
|
|
||||||
<TabsContent key={t} className="h-full relative" value={t}>
|
|
||||||
<div className="h-[70vh] overflow-y-auto">
|
|
||||||
<pre
|
|
||||||
id={t}
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: log ? logToHtml(log) : `no ${t} logs`,
|
|
||||||
}}
|
|
||||||
className="-scroll-mt-24"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
className="absolute bottom-4 right-4"
|
|
||||||
onClick={to_bottom(t)}
|
|
||||||
>
|
|
||||||
<ChevronDown className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TabsContent>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Section>
|
</Section>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const NoSearchLogs = (id: string, tail: string) => {
|
||||||
|
const { data: log, refetch } = useRead(
|
||||||
|
"GetLog",
|
||||||
|
{ deployment: id, tail: Number(tail) },
|
||||||
|
{ refetchInterval: 30000 }
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
Log: <Log log={log} />,
|
||||||
|
refetch,
|
||||||
|
stderr: !!log?.stderr,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const SearchLogs = (id: string, search: string) => {
|
||||||
|
const { data: log, refetch } = useRead(
|
||||||
|
"SearchLog",
|
||||||
|
{ deployment: id, terms: search.split(" ").filter((term) => term) },
|
||||||
|
{ refetchInterval: 30000 }
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
Log: <Log log={log} />,
|
||||||
|
refetch,
|
||||||
|
stderr: !!log?.stderr,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Log = ({ log }: { log: Types.Log | undefined }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{["stdout", "stderr"].map((stream) => {
|
||||||
|
const _log = log?.[stream as keyof typeof log] as string | undefined;
|
||||||
|
return (
|
||||||
|
<TabsContent key={stream} className="h-full relative" value={stream}>
|
||||||
|
<div className="h-[70vh] overflow-y-auto">
|
||||||
|
<pre
|
||||||
|
id={stream}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: _log ? logToHtml(_log) : `no ${stream} logs`,
|
||||||
|
}}
|
||||||
|
className="-scroll-mt-24"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className="absolute bottom-4 right-4"
|
||||||
|
onClick={to_bottom(stream)}
|
||||||
|
>
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TabsContent>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const TailLengthSelector = ({
|
const TailLengthSelector = ({
|
||||||
selected,
|
selected,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
selected: string;
|
selected: string;
|
||||||
onSelect: (value: string) => void;
|
onSelect: (value: string) => void;
|
||||||
|
disabled?: boolean;
|
||||||
}) => (
|
}) => (
|
||||||
<Select value={selected} onValueChange={onSelect}>
|
<Select value={selected} onValueChange={onSelect} disabled={disabled}>
|
||||||
<SelectTrigger>
|
<SelectTrigger className="w-[120px]">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export const Login = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-center items-center container mt-32">
|
<div className="flex justify-center items-center container mt-32">
|
||||||
<Card className="w-full max-w-[500px] place-self-center">
|
<Card className="w-full max-w-[500px] place-self-center">
|
||||||
<CardHeader className="flex justify-between">
|
<CardHeader className="flex-row justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle className="text-xl">Monitor</CardTitle>
|
<CardTitle className="text-xl">Monitor</CardTitle>
|
||||||
<CardDescription>Log In</CardDescription>
|
<CardDescription>Log In</CardDescription>
|
||||||
|
|||||||
Reference in New Issue
Block a user