feat(cli+tauri): Send logs from host to guest (#90)

* feat(tauri): send logs from cli to tauri

---------

Co-authored-by: binarybaron <binarybaron@unstoppableswap.net>
Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com>
This commit is contained in:
Einliterflasche 2024-09-26 13:09:46 +02:00 committed by GitHub
parent 21608ce4f7
commit 7b79ad6abe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 206 additions and 136 deletions

134
Cargo.lock generated
View file

@ -134,9 +134,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arboard"
version = "3.4.0"
version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89"
checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4"
dependencies = [
"clipboard-win",
"core-graphics 0.23.2",
@ -850,9 +850,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.1.15"
version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
dependencies = [
"jobserver",
"libc",
@ -1776,9 +1776,9 @@ dependencies = [
[[package]]
name = "error-code"
version = "3.2.0"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
[[package]]
name = "event-listener"
@ -2803,9 +2803,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.60"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@ -2926,9 +2926,9 @@ dependencies = [
[[package]]
name = "infer"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199"
checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847"
dependencies = [
"cfb",
]
@ -4234,7 +4234,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate 2.0.2",
"proc-macro-crate 1.1.0",
"proc-macro2",
"quote",
"syn 2.0.46",
@ -4329,7 +4329,6 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.6.0",
"block2",
"dispatch",
"libc",
"objc2",
]
@ -4572,9 +4571,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.7.11"
version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95"
checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9"
dependencies = [
"memchr",
"thiserror",
@ -4583,9 +4582,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.7.11"
version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a"
checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0"
dependencies = [
"pest",
"pest_generator",
@ -4593,9 +4592,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.7.11"
version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183"
checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e"
dependencies = [
"pest",
"pest_meta",
@ -4606,9 +4605,9 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.7.11"
version = "2.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f"
checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f"
dependencies = [
"once_cell",
"pest",
@ -4819,9 +4818,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "plist"
@ -5312,9 +5311,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
dependencies = [
"bitflags 2.6.0",
]
@ -5331,9 +5330,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.5"
version = "1.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [
"aho-corasick 1.1.3",
"memchr",
@ -6393,25 +6392,24 @@ dependencies = [
[[package]]
name = "softbuffer"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd"
checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08"
dependencies = [
"bytemuck",
"cfg_aliases",
"core-graphics 0.23.2",
"core-graphics 0.24.0",
"foreign-types",
"js-sys",
"log",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"objc2-quartz-core",
"raw-window-handle",
"redox_syscall 0.5.3",
"redox_syscall 0.5.4",
"wasm-bindgen",
"web-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -6862,9 +6860,9 @@ dependencies = [
[[package]]
name = "tao"
version = "0.29.1"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3a97abbc7d6cfd0720da3e06fcb1cf2ac87cbfdb5bbbce103a1279a211c4d81"
checksum = "82e7ede56f9ef03a0bb384c7b2bed4f3985ee7f3f79ec887c50d8466eec21096"
dependencies = [
"bitflags 2.6.0",
"cocoa",
@ -6918,13 +6916,12 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "tauri"
version = "2.0.0-rc.8"
version = "2.0.0-rc.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8345ccc676ef16e26b61fc0f5340b4e770678b1e1f53f08c69ebdac5e56b422"
checksum = "eb3c3b1c7ac5b72d59da307b84af900a0098c74c9d7369f65018cd8ec0eb50fb"
dependencies = [
"anyhow",
"bytes",
"cocoa",
"dirs",
"dunce",
"embed_plist",
@ -6939,8 +6936,11 @@ dependencies = [
"log",
"mime",
"muda",
"objc",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"percent-encoding",
"plist",
"raw-window-handle",
"reqwest",
"serde",
@ -6967,9 +6967,9 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.0.0-rc.7"
version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5ad5fcfaf02cf79aa6727f6c5df38567d8dce172b00b62690c6bc46c08b7ce"
checksum = "6ff5713e81e02e0b99f5219b275abbd7d2c0cc0f30180e25b1b650e08feeac63"
dependencies = [
"anyhow",
"cargo_toml",
@ -6989,9 +6989,9 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-rc.7"
version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809ef6316726fc72593d296cf6f4e7461326e310c313d6a6c42b6e7f1e2671cf"
checksum = "5370f2591dcc93d4ff08d9dd168f5097f79b34e859883586a409c627544190e3"
dependencies = [
"base64 0.22.1",
"brotli 6.0.0",
@ -7016,9 +7016,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-rc.6"
version = "2.0.0-rc.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1359e8861d210d25731f8b1bfbb4d111dd06406cf73c59659366ef450364d811"
checksum = "19442dc8ee002ab1926586f6aecb90114f3a1226766008b0c9ac2d9fec9eeb7e"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@ -7030,9 +7030,9 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.0.0-rc.7"
version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dded420c86183f592d0fe925ef9447f41e26fa79f0bdfef8d3f17bfbcdbfb7"
checksum = "5e3368e91a98aa55ea4e3e8ccff516bc1ed2f85872c335ec35e9b345469032e0"
dependencies = [
"anyhow",
"glob",
@ -7084,9 +7084,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-rc.7"
version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c72b844f387bfc3341c355f3e16b8cbf4161848fa4e348670effb222cd3ba5"
checksum = "c5f38d8aaa1e81d20e8e208e3e317f81b59fb75c530fbae8a90e72d02001d687"
dependencies = [
"dpi",
"gtk",
@ -7103,15 +7103,17 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-rc.7"
version = "2.0.0-rc.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73accf936a7cd01d1382de7850726fdf6c1f6ab3b01ccb7a0950cb852e332596"
checksum = "cf1ef5171e14c8fe3b5a63e75004c20d057747bc3e7fdc5f8ded625f0b29f5c7"
dependencies = [
"cocoa",
"gtk",
"http 1.1.0",
"jni",
"log",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"percent-encoding",
"raw-window-handle",
"softbuffer",
@ -7127,9 +7129,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.0.0-rc.7"
version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53d9fe87e985b273696ae22ce2b9f099a8f1b44bc8fb127467bda5fcb3e4371"
checksum = "31fe4c9148e1b35225e1c00753f24b517ce00041d02eb4b4d6fd10613a47736c"
dependencies = [
"brotli 6.0.0",
"cargo_metadata",
@ -7728,9 +7730,9 @@ dependencies = [
[[package]]
name = "tray-icon"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "131a65b2cef2081bc14dbcd414c906edbfa3bb5323dd7e748cc298614681196b"
checksum = "044d7738b3d50f288ddef035b793228740ad4d927f5466b0af55dc15e7e03cfe"
dependencies = [
"core-graphics 0.24.0",
"crossbeam-channel",
@ -7951,9 +7953,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.11.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
@ -8036,11 +8038,10 @@ dependencies = [
[[package]]
name = "urlpattern"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609"
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
dependencies = [
"derive_more",
"regex",
"serde",
"unic-ucd-ident",
@ -8450,12 +8451,13 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window-vibrancy"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8cdd6999298d969289d8078dae02ce798ad23452075985cccba8b6326711ecf"
checksum = "3ea403deff7b51fff19e261330f71608ff2cdef5721d72b64180bb95be7c4150"
dependencies = [
"cocoa",
"objc",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"raw-window-handle",
"windows-sys 0.59.0",
"windows-version",
@ -8850,9 +8852,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.42.0"
version = "0.43.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b8049c8f239cdbfaaea4bacb9646f6b208938ceec0acd5b3e99cd05f70903f"
checksum = "f4d715cf5fe88e9647f3d17b207b6d060d4a88e7171d4ccb2d2c657dd1d44728"
dependencies = [
"base64 0.22.1",
"block",

View file

@ -37,7 +37,7 @@ export function parseCliLogString(log: string): CliLog | string {
} else {
return log;
}
} catch (err) {
} catch {
return log;
}
}

View file

@ -1,7 +1,6 @@
import {
Avatar,
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
@ -15,14 +14,9 @@ import {
import AddIcon from "@material-ui/icons/Add";
import SearchIcon from "@material-ui/icons/Search";
import { ExtendedProviderStatus } from "models/apiModel";
import { RpcMethod } from "models/rpcModel";
import { useState } from "react";
import { setSelectedProvider } from "store/features/providersSlice";
import {
useAllProviders,
useAppDispatch,
useIsRpcEndpointBusy,
} from "store/hooks";
import { useAllProviders, useAppDispatch } from "store/hooks";
import ListSellersDialog from "../listSellers/ListSellersDialog";
import ProviderInfo from "./ProviderInfo";
import ProviderSubmitDialog from "./ProviderSubmitDialog";
@ -65,13 +59,11 @@ export function ProviderSubmitDialogOpenButton() {
export function ListSellersDialogOpenButton() {
const [open, setOpen] = useState(false);
const running = useIsRpcEndpointBusy(RpcMethod.LIST_SELLERS);
return (
<ListItem
autoFocus
button
disabled={running}
onClick={() => {
// Prevents background from being clicked and reopening dialog
if (!open) {
@ -81,7 +73,9 @@ export function ListSellersDialogOpenButton() {
>
<ListSellersDialog open={open} onClose={() => setOpen(false)} />
<ListItemAvatar>
<Avatar>{running ? <CircularProgress /> : <SearchIcon />}</Avatar>
<Avatar>
<SearchIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary="Discover providers by connecting to a rendezvous point" />
</ListItem>

View file

@ -3,7 +3,7 @@ import FolderOpenIcon from "@material-ui/icons/FolderOpen";
import PlayArrowIcon from "@material-ui/icons/PlayArrow";
import StopIcon from "@material-ui/icons/Stop";
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
import { useIsContextAvailable } from "store/hooks";
import { useAppSelector, useIsContextAvailable } from "store/hooks";
import InfoBox from "../../modal/swap/InfoBox";
import CliLogsBox from "../../other/RenderedCliLog";
@ -18,17 +18,16 @@ const useStyles = makeStyles((theme) => ({
export default function RpcControlBox() {
const isRunning = useIsContextAvailable();
const classes = useStyles();
const logs = useAppSelector((s) => s.rpc.logs);
return (
<InfoBox
title={`Daemon Controller`}
mainContent={
isRunning ? (
<CliLogsBox
label="Swap Daemon Logs (current session only)"
logs={[]}
/>
) : null
<CliLogsBox
label="Swap Daemon Logs (current session only)"
logs={logs}
/>
}
additionalContent={
<Box className={classes.actionsOuter}>

View file

@ -3,7 +3,7 @@ import SendIcon from "@material-ui/icons/Send";
import { RpcMethod } from "models/rpcModel";
import { useState } from "react";
import { SatsAmount } from "renderer/components/other/Units";
import { useAppSelector, useIsRpcEndpointBusy } from "store/hooks";
import { useAppSelector } from "store/hooks";
import BitcoinIcon from "../../icons/BitcoinIcon";
import InfoBox from "../../modal/swap/InfoBox";
import WithdrawDialog from "../../modal/wallet/WithdrawDialog";
@ -20,7 +20,6 @@ const useStyles = makeStyles((theme) => ({
export default function WithdrawWidget() {
const classes = useStyles();
const walletBalance = useAppSelector((state) => state.rpc.state.balance);
const checkingBalance = useIsRpcEndpointBusy(RpcMethod.GET_BTC_BALANCE);
const [showDialog, setShowDialog] = useState(false);
function onShowDialog() {
@ -50,7 +49,7 @@ export default function WithdrawWidget() {
size="large"
onClick={onShowDialog}
disabled={
walletBalance === null || checkingBalance || walletBalance <= 0
walletBalance === null || walletBalance <= 0
}
>
Withdraw

View file

@ -5,6 +5,7 @@ import {
BalanceResponse,
BuyXmrArgs,
BuyXmrResponse,
CliLogEmittedEvent,
GetLogsArgs,
GetLogsResponse,
GetSwapInfoResponse,
@ -20,6 +21,7 @@ import {
} from "models/tauriModel";
import {
contextStatusEventReceived,
receivedCliLog,
rpcSetBalance,
rpcSetSwapInfo,
} from "store/features/rpcSlice";
@ -47,6 +49,11 @@ export async function initEventListeners() {
console.log("Received context init progress event", event.payload);
store.dispatch(contextStatusEventReceived(event.payload));
});
listen<CliLogEmittedEvent>("cli-log-emitted", (event) => {
console.log("Received cli log event", event.payload);
store.dispatch(receivedCliLog(event.payload))
})
}
async function invoke<ARGS, RESPONSE>(

View file

@ -1,11 +1,14 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ExtendedProviderStatus, ProviderStatus } from "models/apiModel";
import {
CliLogEmittedEvent,
GetSwapInfoResponse,
TauriContextStatusEvent,
} from "models/tauriModel";
import { MoneroRecoveryResponse } from "../../models/rpcModel";
import { GetSwapInfoResponseExt } from "models/tauriModelExt";
import { getLogsAndStringsFromRawFileString } from "utils/parseUtils";
import { CliLog } from "models/cliModel";
interface State {
balance: number | null;
@ -27,7 +30,7 @@ interface State {
export interface RPCSlice {
status: TauriContextStatusEvent | null;
state: State;
busyEndpoints: string[];
logs: (CliLog | string)[];
}
const initialState: RPCSlice = {
@ -42,13 +45,18 @@ const initialState: RPCSlice = {
updateState: false,
},
},
busyEndpoints: [],
logs: [],
};
export const rpcSlice = createSlice({
name: "rpc",
initialState,
reducers: {
receivedCliLog(slice, action: PayloadAction<CliLogEmittedEvent>) {
const buffer = action.payload.buffer;
const logs = getLogsAndStringsFromRawFileString(buffer);
slice.logs = slice.logs.concat(logs);
},
contextStatusEventReceived(
slice,
action: PayloadAction<TauriContextStatusEvent>,
@ -74,17 +82,6 @@ export const rpcSlice = createSlice({
slice.state.swapInfos[action.payload.swap_id] =
action.payload as GetSwapInfoResponseExt;
},
rpcSetEndpointBusy(slice, action: PayloadAction<string>) {
if (!slice.busyEndpoints.includes(action.payload)) {
slice.busyEndpoints.push(action.payload);
}
},
rpcSetEndpointFree(slice, action: PayloadAction<string>) {
const index = slice.busyEndpoints.indexOf(action.payload);
if (index >= 0) {
slice.busyEndpoints.splice(index);
}
},
rpcSetMoneroRecoveryKeys(
slice,
action: PayloadAction<[string, MoneroRecoveryResponse]>,
@ -105,11 +102,10 @@ export const rpcSlice = createSlice({
export const {
contextStatusEventReceived,
receivedCliLog,
rpcSetBalance,
rpcSetWithdrawTxId,
rpcResetWithdrawTxId,
rpcSetEndpointBusy,
rpcSetEndpointFree,
rpcSetRendezvousDiscoveredProviders,
rpcSetSwapInfo,
rpcSetMoneroRecoveryKeys,

View file

@ -42,10 +42,6 @@ export function useActiveSwapInfo() {
return useSwapInfo(swapId);
}
export function useIsRpcEndpointBusy(method: string) {
return useAppSelector((state) => state.rpc.busyEndpoints.includes(method));
}
export function useAllProviders() {
return useAppSelector((state) => {
const registryProviders = state.providers.registry.providers || [];

View file

@ -1,4 +1,4 @@
import { CliLog } from "models/cliModel";
import { CliLog, parseCliLogString } from "models/cliModel";
import { Multiaddr } from "multiaddr";
/*
@ -55,19 +55,7 @@ export function getLinesOfString(data: string): string[] {
export function getLogsAndStringsFromRawFileString(
rawFileData: string,
): (CliLog | string)[] {
return getLinesOfString(rawFileData).map((line) => {
try {
return JSON.parse(line);
} catch {
return line;
}
});
}
export function getLogsFromRawFileString(rawFileData: string): CliLog[] {
// TODO: Reimplement this using Tauri
return [];
return getLogsAndStringsFromRawFileString(rawFileData).filter(isCliLog);
return getLinesOfString(rawFileData).map(parseCliLogString);
}
export function logsToRawString(logs: (CliLog | string)[]): string {

View file

@ -138,15 +138,12 @@ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
match context {
Ok(context) => {
let state = app_handle.state::<RwLock<State>>();
state.write().await.set_context(Arc::new(context));
// To display to the user that the setup is done, we emit an event to the Tauri frontend
tauri_handle.emit_context_init_progress_event(TauriContextStatusEvent::Available);
}
Err(e) => {
println!("Error while initializing context: {:?}", e);
// To display to the user that the setup failed, we emit an event to the Tauri frontend
tauri_handle.emit_context_init_progress_event(TauriContextStatusEvent::Failed);
}
@ -182,7 +179,8 @@ pub fn run() {
RunEvent::Exit | RunEvent::ExitRequested { .. } => {
// Here we cleanup the Context when the application is closed
// This is necessary to among other things stop the monero-wallet-rpc process
// If the application is forcibly closed, this may not be called
// If the application is forcibly closed, this may not be called.
// TODO: fix that
let context = app.state::<RwLock<State>>().inner().try_read();
match context {

View file

@ -81,7 +81,8 @@ pub async fn main() -> Result<()> {
// initialize tracing
let format = if json { Format::Json } else { Format::Raw };
let log_dir = config.data.dir.join("logs");
common::tracing_util::init(LevelFilter::DEBUG, format, log_dir).expect("initialize tracing");
common::tracing_util::init(LevelFilter::DEBUG, format, log_dir, None)
.expect("initialize tracing");
// check for conflicting env / config values
if config.monero.network != env_config.monero_network {

View file

@ -288,7 +288,12 @@ impl ContextBuilder {
};
START.call_once(|| {
let _ = common::tracing_util::init(level_filter, format, data_dir.join("logs"));
let _ = common::tracing_util::init(
level_filter,
format,
data_dir.join("logs"),
self.tauri_handle.clone(),
);
});
let seed = Seed::from_file_or_generate(data_dir.as_path())

View file

@ -6,8 +6,9 @@ use strum::Display;
use typeshare::typeshare;
use uuid::Uuid;
static SWAP_PROGRESS_EVENT_NAME: &str = "swap-progress-update";
static CONTEXT_INIT_PROGRESS_EVENT_NAME: &str = "context-init-progress-update";
const SWAP_PROGRESS_EVENT_NAME: &str = "swap-progress-update";
const CONTEXT_INIT_PROGRESS_EVENT_NAME: &str = "context-init-progress-update";
const CLI_LOG_EMITTED_EVENT_NAME: &str = "cli-log-emitted";
#[derive(Debug, Clone)]
pub struct TauriHandle(
@ -47,6 +48,12 @@ pub trait TauriEmitter {
fn emit_context_init_progress_event(&self, event: TauriContextStatusEvent) {
let _ = self.emit_tauri_event(CONTEXT_INIT_PROGRESS_EVENT_NAME, event);
}
fn emit_cli_log_event(&self, event: CliLogEmittedEvent) {
let _ = self
.emit_tauri_event(CLI_LOG_EMITTED_EVENT_NAME, event)
.ok();
}
}
impl TauriEmitter for TauriHandle {
@ -158,3 +165,14 @@ pub enum TauriSwapProgressEvent {
},
Released,
}
/// This event is emitted whenever there is a log message issued in the CLI.
///
/// It contains a json serialized object containing the log message and metadata.
#[typeshare]
#[derive(Debug, Serialize, Clone)]
#[typeshare]
pub struct CliLogEmittedEvent {
/// The serialized object containing the log message and metadata.
pub buffer: String
}

View file

@ -1,13 +1,17 @@
use std::io;
use std::path::Path;
use std::str::FromStr;
use anyhow::Result;
use tracing_subscriber::filter::{Directive, LevelFilter};
use tracing_subscriber::fmt::time::UtcTime;
use tracing_subscriber::fmt::MakeWriter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, EnvFilter, Layer};
use crate::cli::api::tauri_bindings::{CliLogEmittedEvent, TauriEmitter, TauriHandle};
/// Output formats for logging messages.
pub enum Format {
/// Standard, human readable format.
@ -20,11 +24,12 @@ pub enum Format {
/// Besides printing to `stdout`, this will append to a log file.
/// Said file will contain JSON-formatted logs of all levels,
/// disregarding the arguments to this function.
pub fn init(level_filter: LevelFilter, format: Format, dir: impl AsRef<Path>) -> Result<()> {
let env_filter = EnvFilter::from_default_env()
.add_directive(Directive::from_str(&format!("asb={}", &level_filter))?)
.add_directive(Directive::from_str(&format!("swap={}", &level_filter))?);
pub fn init(
level_filter: LevelFilter,
format: Format,
dir: impl AsRef<Path>,
tauri_handle: Option<TauriHandle>,
) -> Result<()> {
// file logger will always write in JSON format and with timestamps
let file_appender = tracing_appender::rolling::never(&dir, "swap-all.log");
@ -34,9 +39,9 @@ pub fn init(level_filter: LevelFilter, format: Format, dir: impl AsRef<Path>) ->
.with_timer(UtcTime::rfc_3339())
.with_target(false)
.json()
.with_filter(env_filter);
.with_filter(env_filter(level_filter)?);
// terminal logger
// terminal loger
let is_terminal = atty::is(atty::Stream::Stderr);
let terminal_layer = fmt::layer()
.with_writer(std::io::stdout)
@ -44,21 +49,83 @@ pub fn init(level_filter: LevelFilter, format: Format, dir: impl AsRef<Path>) ->
.with_timer(UtcTime::rfc_3339())
.with_target(false);
// tauri layer (forwards logs to the tauri guest when connected)
let tauri_layer = fmt::layer()
.with_writer(TauriWriter::new(tauri_handle))
.with_ansi(false)
.with_timer(UtcTime::rfc_3339())
.with_target(false)
.json()
.with_filter(env_filter(level_filter)?);
// combine the layers and start logging, format with json if specified
if let Format::Json = format {
tracing_subscriber::registry()
.with(file_layer)
.with(tauri_layer)
.with(terminal_layer.json().with_filter(level_filter))
.init();
} else {
tracing_subscriber::registry()
.with(file_layer)
.with(tauri_layer)
.with(terminal_layer.with_filter(level_filter))
.init();
}
// now we can use the tracing macros to log messages
// Now we can use the tracing macros to log messages
tracing::info!(%level_filter, logs_dir=%dir.as_ref().display(), "Initialized tracing");
Ok(())
}
/// This function controls which crate's logs actually get logged and from which level.
fn env_filter(level_filter: LevelFilter) -> Result<EnvFilter> {
Ok(EnvFilter::from_default_env()
.add_directive(Directive::from_str(&format!("asb={}", &level_filter))?)
.add_directive(Directive::from_str(&format!("swap={}", &level_filter))?))
}
/// A writer that forwards tracing log messages to the tauri guest.
#[derive(Clone)]
pub struct TauriWriter {
tauri_handle: Option<TauriHandle>,
}
impl TauriWriter {
/// Create a new Tauri writer that sends log messages to the tauri guest.
pub fn new(tauri_handle: Option<TauriHandle>) -> Self {
Self { tauri_handle }
}
}
/// This is needed for tracing to accept this as a writer.
impl<'a> MakeWriter<'a> for TauriWriter {
type Writer = TauriWriter;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
/// For every write issued by tracing we simply pass the string on as an event to the tauri guest.
impl std::io::Write for TauriWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
// Since this function accepts bytes, we need to pass to utf8 first
let owned_buf = buf.to_owned();
let utf8_string = String::from_utf8(owned_buf)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
// Then send to tauri
self.tauri_handle.emit_cli_log_event(CliLogEmittedEvent {
buffer: utf8_string,
});
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
// No-op, we don't need to flush anything
Ok(())
}
}