Add log sending
This commit is contained in:
parent
2d89881aac
commit
db68aa4a68
8 changed files with 131 additions and 13 deletions
1
.env
1
.env
|
|
@ -9,6 +9,7 @@ VITE_PLATFORM_PRIVACY=https://flotilla.social/privacy
|
|||
VITE_PLATFORM_NAME=Flotilla
|
||||
VITE_PLATFORM_LOGO=static/logo.png
|
||||
VITE_PLATFORM_RELAYS=
|
||||
VITE_PLATFORM_LOGEE=be523f2d2255fa96b281233ba9df60d63ef74f874bea96651edd4e1f79785703
|
||||
VITE_PLATFORM_ACCENT="#7161FF"
|
||||
VITE_THEME="clay"
|
||||
VITE_PLATFORM_DESCRIPTION="The chat app built for self-hosted communities."
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ You can also optionally create an `.env.local` file and populate it with the fol
|
|||
- `VITE_PLATFORM_DESCRIPTION` - A description of the app
|
||||
- `VITE_PLATFORM_TERMS` - URL to your terms of service page
|
||||
- `VITE_PLATFORM_PRIVACY` - URL to your privacy policy page
|
||||
- `VITE_PLATFORM_LOGEE` - A hex pubkey which will receive logs users send from their privacy settings
|
||||
|
||||
**Platform mode**
|
||||
- `VITE_PLATFORM_RELAYS` - A comma-separated list of relay urls that will make flotilla operate in "platform mode". Disables all space browse/add/select functionality and makes the first platform relay the home page.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
Domain,
|
||||
FollowLists,
|
||||
Handles,
|
||||
Logger,
|
||||
MessagingRelayLists,
|
||||
MuteLists,
|
||||
Network,
|
||||
|
|
@ -142,6 +143,7 @@ export const deletes = usePlugin(Deletes)
|
|||
export const domain = usePlugin(Domain)
|
||||
export const followLists = usePlugin(FollowLists)
|
||||
export const handles = usePlugin(Handles)
|
||||
export const logger = usePlugin(Logger)
|
||||
export const messagingRelayLists = usePlugin(MessagingRelayLists)
|
||||
export const muteLists = usePlugin(MuteLists)
|
||||
export const network = usePlugin(Network)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ export const PLATFORM_LOGO = import.meta.env.PROD
|
|||
|
||||
export const PLATFORM_NAME = env("VITE_PLATFORM_NAME")
|
||||
|
||||
export const PLATFORM_LOGEE = env("VITE_PLATFORM_LOGEE")
|
||||
|
||||
export const PLATFORM_ACCENT = env("VITE_PLATFORM_ACCENT")
|
||||
|
||||
// components visual preset (see src/lib/components/theme.css). Selected per
|
||||
|
|
|
|||
86
src/app/logger.ts
Normal file
86
src/app/logger.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import type {Unsubscriber} from "svelte/store"
|
||||
import {Capacitor} from "@capacitor/core"
|
||||
import {tryCatch} from "@welshman/lib"
|
||||
import type {Maybe} from "@welshman/lib"
|
||||
import {DirectMessage} from "@welshman/domain"
|
||||
import type {Logger} from "@welshman/app"
|
||||
import {logger, wraps, writer} from "@app/core"
|
||||
import {PLATFORM_LOGEE} from "@app/env"
|
||||
|
||||
const formatValue = (value: unknown) => {
|
||||
if (value instanceof Error) return value.stack || value.message
|
||||
if (typeof value === "string") return value
|
||||
|
||||
return tryCatch(() => JSON.stringify(value)) || String(value)
|
||||
}
|
||||
|
||||
const bindLogging = ($logger: Logger) => {
|
||||
const {error} = console
|
||||
|
||||
const log = (source: string, value: unknown) => $logger.log(source, {message: formatValue(value)})
|
||||
|
||||
const onError = (event: ErrorEvent) => log("error", event.error || event.message)
|
||||
|
||||
const onUnhandledRejection = (event: PromiseRejectionEvent) => log("rejection", event.reason)
|
||||
|
||||
console.error = (...args: unknown[]) => {
|
||||
log("console", args.map(formatValue).join(" "))
|
||||
error(...args)
|
||||
}
|
||||
|
||||
$logger.log("app", {
|
||||
platform: Capacitor.getPlatform(),
|
||||
build: import.meta.env.VITE_BUILD_HASH,
|
||||
userAgent: navigator.userAgent,
|
||||
})
|
||||
|
||||
window.addEventListener("error", onError)
|
||||
window.addEventListener("unhandledrejection", onUnhandledRejection)
|
||||
|
||||
return () => {
|
||||
console.error = error
|
||||
window.removeEventListener("error", onError)
|
||||
window.removeEventListener("unhandledrejection", onUnhandledRejection)
|
||||
}
|
||||
}
|
||||
|
||||
// Errors are only useful after the fact if someone kept them around, so collect them into the
|
||||
// app's logger, which the user can hand over via `sendLogs` when something goes wrong. Logging in
|
||||
// swaps in a fresh app with an empty log, so re-bind against the new one when that happens.
|
||||
export const setupLogging = () => {
|
||||
let unbind: Maybe<Unsubscriber>
|
||||
|
||||
const unsubscribe = logger.subscribe($logger => {
|
||||
unbind?.()
|
||||
unbind = bindLogging($logger)
|
||||
})
|
||||
|
||||
return () => {
|
||||
unbind?.()
|
||||
unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
export const sendLogs = async () => {
|
||||
const messages = logger.get().messages.get()
|
||||
const lines: string[] = []
|
||||
let size = 0
|
||||
|
||||
// Relays commonly cap events at 64kb, so send only as much recent history as will fit
|
||||
for (const message of messages.slice().reverse()) {
|
||||
const line = JSON.stringify(message)
|
||||
|
||||
size += line.length
|
||||
|
||||
if (size > 32_000) break
|
||||
|
||||
lines.unshift(line)
|
||||
}
|
||||
|
||||
const event = await writer(DirectMessage)
|
||||
.setContent(lines.join("\n"))
|
||||
.addRecipient(PLATFORM_LOGEE)
|
||||
.renderTemplate()
|
||||
|
||||
return wraps.get().publish({event, recipients: [PLATFORM_LOGEE], pow: 16})
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ export type SettingsValues = {
|
|||
hide_sensitive: boolean
|
||||
trusted_relays: string[]
|
||||
report_usage: boolean
|
||||
report_errors: boolean
|
||||
relay_auth: RelayAuthMode
|
||||
send_delay: number
|
||||
font_size: number
|
||||
|
|
@ -39,7 +38,6 @@ export const defaultSettings: SettingsValues = {
|
|||
hide_sensitive: true,
|
||||
trusted_relays: [],
|
||||
report_usage: true,
|
||||
report_errors: true,
|
||||
relay_auth: RelayAuthMode.Conservative,
|
||||
send_delay: 0,
|
||||
font_size: 1.1,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
import {app} from "@app/core"
|
||||
import {setupHistory} from "@app/routes"
|
||||
import {setupAnalytics} from "@app/analytics"
|
||||
import {setupLogging} from "@app/logger"
|
||||
import "@app/policies"
|
||||
import {restoreSession} from "@app/session"
|
||||
import {signerRequests} from "@app/signer"
|
||||
|
|
@ -128,6 +129,9 @@
|
|||
// Attach the user before anything reads or decrypts on their behalf
|
||||
unsubscribers.push(await restoreSession())
|
||||
|
||||
// Set up logging
|
||||
unsubscribers.push(setupLogging())
|
||||
|
||||
// Sync stuff to storage
|
||||
await Promise.all([
|
||||
sync({
|
||||
|
|
|
|||
|
|
@ -7,14 +7,33 @@
|
|||
import Card from "@lib/components/Card.svelte"
|
||||
import PageContent from "@lib/components/PageContent.svelte"
|
||||
import Button from "@lib/components/Button.svelte"
|
||||
import Spinner from "@lib/components/Spinner.svelte"
|
||||
import {pushToast} from "@app/toast"
|
||||
import {PLATFORM_NAME} from "@app/env"
|
||||
import {sendLogs} from "@app/logger"
|
||||
import {RelayAuthMode, userSettingsValues, publishSettings} from "@app/settings"
|
||||
|
||||
const reset = () => {
|
||||
settings = {...$userSettingsValues}
|
||||
}
|
||||
|
||||
const send = async () => {
|
||||
loading = true
|
||||
|
||||
try {
|
||||
const thunk = await sendLogs()
|
||||
const error = await thunk.waitForError()
|
||||
|
||||
if (error) {
|
||||
pushToast({theme: "error", message: error})
|
||||
} else {
|
||||
pushToast({message: "Your logs have been sent. Thank you!"})
|
||||
}
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
const onAuthModeChange = (checked: boolean) => {
|
||||
settings.relay_auth = checked ? RelayAuthMode.Aggressive : RelayAuthMode.Conservative
|
||||
}
|
||||
|
|
@ -26,6 +45,7 @@
|
|||
})
|
||||
|
||||
let settings = $state({...$userSettingsValues})
|
||||
let loading = $state(false)
|
||||
</script>
|
||||
|
||||
<form {onsubmit}>
|
||||
|
|
@ -48,17 +68,6 @@
|
|||
<p>Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.</p>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<FieldInline>
|
||||
{#snippet label()}
|
||||
<p>Report errors?</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<ToggleInput bind:checked={settings.report_errors} />
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<p>Allow {PLATFORM_NAME} to send error reports to help improve the app.</p>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<FieldInline>
|
||||
{#snippet label()}
|
||||
<p>Report usage?</p>
|
||||
|
|
@ -70,6 +79,21 @@
|
|||
<p>Allow {PLATFORM_NAME} to collect anonymous usage data.</p>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
<FieldInline>
|
||||
{#snippet label()}
|
||||
<p>Something went wrong?</p>
|
||||
{/snippet}
|
||||
{#snippet input()}
|
||||
<Button class="button button-neutral" onclick={send} disabled={loading}>
|
||||
<Spinner {loading}>Send Logs</Spinner>
|
||||
</Button>
|
||||
{/snippet}
|
||||
{#snippet info()}
|
||||
<p>
|
||||
Send a record of recent app activity to the {PLATFORM_NAME} team as a direct message.
|
||||
</p>
|
||||
{/snippet}
|
||||
</FieldInline>
|
||||
</Card>
|
||||
<Card class="sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
||||
<Button class="button button-neutral" onclick={reset}>Discard Changes</Button>
|
||||
|
|
|
|||
Loading…
Reference in a new issue