mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 23:34:10 +02:00
608 lines
20 KiB
HTML
608 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Configuration | EchoHub Documentation </title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="title" content="Configuration | EchoHub Documentation ">
|
|
|
|
|
|
<link rel="icon" href="../images/hue_icon.svg">
|
|
<link rel="stylesheet" href="../public/docfx.min.css">
|
|
<link rel="stylesheet" href="../public/main.css">
|
|
<meta name="docfx:navrel" content="../toc.html">
|
|
<meta name="docfx:tocrel" content="toc.html">
|
|
|
|
<meta name="docfx:rel" content="../">
|
|
|
|
|
|
<meta name="docfx:docurl" content="https://github.com/HueByte/EchoHub/blob/master/docs/#L1">
|
|
<meta name="loc:inThisArticle" content="In this article">
|
|
<meta name="loc:searchResultsCount" content="{count} results for "{query}"">
|
|
<meta name="loc:searchNoResults" content="No results for "{query}"">
|
|
<meta name="loc:tocFilter" content="Filter by title">
|
|
<meta name="loc:nextArticle" content="Next">
|
|
<meta name="loc:prevArticle" content="Previous">
|
|
<meta name="loc:themeLight" content="Light">
|
|
<meta name="loc:themeDark" content="Dark">
|
|
<meta name="loc:themeAuto" content="Auto">
|
|
<meta name="loc:changeTheme" content="Change theme">
|
|
<meta name="loc:copy" content="Copy">
|
|
<meta name="loc:downloadPdf" content="Download PDF">
|
|
|
|
<script type="module" src="./../public/docfx.min.js"></script>
|
|
|
|
<script>
|
|
const theme = localStorage.getItem('theme') || 'auto'
|
|
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
|
|
</script>
|
|
|
|
</head>
|
|
|
|
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
|
|
<header class="bg-body border-bottom">
|
|
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
|
|
<div class="container-xxl flex-nowrap">
|
|
<a class="navbar-brand" href="../index.html">
|
|
<img id="logo" class="svg" src="../images/hue_icon.svg" alt="EchoHub">
|
|
EchoHub
|
|
</a>
|
|
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
|
|
<i class="bi bi-three-dots"></i>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navpanel">
|
|
<div id="navbar">
|
|
<form class="search" role="search" id="search">
|
|
<i class="bi bi-search"></i>
|
|
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="container-xxl">
|
|
<div class="toc-offcanvas">
|
|
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
|
|
<div class="offcanvas-header">
|
|
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
|
|
</div>
|
|
<div class="offcanvas-body">
|
|
<nav class="toc" id="toc"></nav>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div class="actionbar">
|
|
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
|
|
<i class="bi bi-list"></i>
|
|
</button>
|
|
|
|
<nav id="breadcrumb"></nav>
|
|
</div>
|
|
|
|
<article data-uid="">
|
|
<h1 id="configuration">Configuration</h1>
|
|
|
|
<p>EchoHub Server generates an <code>appsettings.json</code> with sensible defaults on first run (including a random JWT secret), so you can launch and start chatting immediately. Tweak things later when you feel like it.</p>
|
|
<div class="NOTE">
|
|
<h5>Note</h5>
|
|
<p>Under the hood, EchoHub Server is built on ASP.NET Core, so it inherits the standard .NET configuration system. If you're familiar with that, everything works exactly as you'd expect. If not — no worries, this page covers everything you need.</p>
|
|
</div>
|
|
<h2 id="how-it-works">How It Works</h2>
|
|
<p>EchoHub Server loads settings from multiple sources. Each source <strong>overrides</strong> the previous one, so you can layer defaults with environment-specific values:</p>
|
|
<pre><code class="lang-text">1. appsettings.json (base defaults)
|
|
2. appsettings.{Environment}.json (e.g. appsettings.Production.json)
|
|
3. Environment variables (great for Docker / CI)
|
|
4. Command-line arguments (highest priority)
|
|
</code></pre>
|
|
<p>The last one wins. If <code>appsettings.json</code> says <code>"Irc:Port": 6667</code> but you pass <code>--Irc:Port=7000</code> on the command line, port 7000 is what you get.</p>
|
|
<p>In practice this means you can leave <code>appsettings.json</code> alone and override just the settings you care about using environment variables or CLI flags — no need to edit JSON files if that's not your thing.</p>
|
|
<h3 id="environment-variable-mapping">Environment Variable Mapping</h3>
|
|
<p>Environment variables use <strong>double underscores</strong> (<code>__</code>) in place of the JSON nesting. The rule is simple — replace every <code>:</code> (or each level of JSON nesting) with <code>__</code>:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>appsettings.json path</th>
|
|
<th>Environment variable</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Server:Name</code></td>
|
|
<td><code>Server__Name</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:Enabled</code></td>
|
|
<td><code>Irc__Enabled</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Jwt:Secret</code></td>
|
|
<td><code>Jwt__Secret</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Serilog:MinimumLevel:Default</code></td>
|
|
<td><code>Serilog__MinimumLevel__Default</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ConnectionStrings:DefaultConnection</code></td>
|
|
<td><code>ConnectionStrings__DefaultConnection</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Arrays use numeric indices: <code>Server:Admins:0</code> becomes <code>Server__Admins__0</code>, <code>Server:Admins:1</code> becomes <code>Server__Admins__1</code>, and so on.</p>
|
|
<p>This is why the Docker <code>.env</code> file uses <code>Server__Name=My Server</code> instead of JSON — Docker passes these as environment variables, and the server picks them up automatically.</p>
|
|
<h3 id="examples">Examples</h3>
|
|
<p>All three of these achieve the same thing — use whichever fits your setup.</p>
|
|
<p><strong>appsettings.json</strong> (direct editing):</p>
|
|
<pre><code class="lang-json">{
|
|
"Server": {
|
|
"Name": "My EchoHub Server",
|
|
"PublicServer": true
|
|
}
|
|
}
|
|
</code></pre>
|
|
<p><strong>Environment variables</strong> (Docker, systemd, shell export):</p>
|
|
<pre><code class="lang-bash">export Server__Name="My EchoHub Server"
|
|
export Server__PublicServer=true
|
|
</code></pre>
|
|
<p><strong>Command-line arguments</strong> (quick overrides, highest priority):</p>
|
|
<pre><code class="lang-bash">./EchoHub.Server --Server:Name="My EchoHub Server" --Irc:Enabled=true
|
|
</code></pre>
|
|
<h2 id="configuration-reference">Configuration Reference</h2>
|
|
<p>The full <code>appsettings.json</code> is auto-generated on first run from the <a href="https://github.com/HueByte/EchoHub/blob/master/src/EchoHub.Server/appsettings.example.json">example config</a>. Here's every option:</p>
|
|
<h3 id="general">General</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Urls</code></td>
|
|
<td><code>http://0.0.0.0:5000</code></td>
|
|
<td>Listen address and port</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>AllowedHosts</code></td>
|
|
<td><code>*</code></td>
|
|
<td>Allowed host headers (leave <code>*</code> unless you need host filtering)</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="database">Database</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>ConnectionStrings:DefaultConnection</code></td>
|
|
<td><em>(empty)</em></td>
|
|
<td>SQLite connection string. Empty = <code>echohub.db</code> in the app directory</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="authentication">Authentication</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Jwt:Secret</code></td>
|
|
<td><em>(auto-generated)</em></td>
|
|
<td>Signing key (min 32 chars). Auto-generated on first run</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Jwt:Issuer</code></td>
|
|
<td><code>EchoHub.Server</code></td>
|
|
<td>JWT issuer claim</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Jwt:Audience</code></td>
|
|
<td><code>EchoHub.Client</code></td>
|
|
<td>JWT audience claim</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Access tokens expire after 15 minutes, refresh tokens after 30 days with rotation on each use.</p>
|
|
<h3 id="server-identity">Server Identity</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Server:Name</code></td>
|
|
<td><code>My EchoHub Server</code></td>
|
|
<td>Display name shown to clients</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:Description</code></td>
|
|
<td><code>A self-hosted EchoHub chat server</code></td>
|
|
<td>Server description</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:PublicServer</code></td>
|
|
<td><code>false</code></td>
|
|
<td>Register on the <a href="https://echohub.voidcube.cloud/servers">public directory</a></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:PublicHosts</code></td>
|
|
<td><code>[]</code></td>
|
|
<td>Hostnames advertised to the directory (array, e.g. <code>["chat.example.com"]</code>). Required when <code>PublicServer</code> is <code>true</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:Tags</code></td>
|
|
<td><code>[]</code></td>
|
|
<td>Topic tags surfaced in the directory browser (array, e.g. <code>["community", "gaming"]</code>)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:Admins</code></td>
|
|
<td><code>[]</code></td>
|
|
<td>Array of admin usernames (e.g. <code>["alice", "bob"]</code>)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:Registration</code></td>
|
|
<td><code>open</code></td>
|
|
<td>Registration mode: <code>open</code>, <code>invite</code> (codes via <code>/invite</code>, Admin+), or <code>closed</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Server:DirectoryClaimPath</code></td>
|
|
<td><em>(empty)</em></td>
|
|
<td>Overrides where the directory claim token file is stored. Empty = <code>directory-claim.json</code> next to the SQLite database. Treat it as a secret and back it up with the database</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="uploads">Uploads</h3>
|
|
<p>Per-attachment size limits by kind (in megabytes) and the per-message attachment cap. An
|
|
absent or partial <code>Uploads</code> section keeps the built-in defaults. See
|
|
<a href="messages-and-attachments.html">Messages & Attachments</a> for how kinds are detected.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Uploads:MaxImageSizeMB</code></td>
|
|
<td><code>10</code></td>
|
|
<td>Max size for one image attachment</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Uploads:MaxAudioSizeMB</code></td>
|
|
<td><code>10</code></td>
|
|
<td>Max size for one audio attachment</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Uploads:MaxFileSizeMB</code></td>
|
|
<td><code>100</code></td>
|
|
<td>Max size for any other attachment</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Uploads:MaxAvatarSizeMB</code></td>
|
|
<td><code>2</code></td>
|
|
<td>Max avatar upload size</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Uploads:MaxAttachmentsPerMessage</code></td>
|
|
<td><code>10</code></td>
|
|
<td>Attachments allowed on a single message</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>The server sizes its request-body limits from these values, so raising a limit here is all
|
|
that's needed — no separate Kestrel tuning.</p>
|
|
<h3 id="spam-protection">Spam Protection</h3>
|
|
<p>Per-user flood, duplicate, join, and channel-create limits with auto-mute escalation. Mods and
|
|
above are always exempt, and the defaults are lenient enough that a fast typist never trips them.
|
|
An absent or partial <code>Spam</code> section keeps these defaults.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Spam:Enabled</code></td>
|
|
<td><code>true</code></td>
|
|
<td>Master switch for all spam protection</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:MaxMessagesPerWindow</code></td>
|
|
<td><code>8</code></td>
|
|
<td>Max messages per <code>WindowSeconds</code> before a send is rejected</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:WindowSeconds</code></td>
|
|
<td><code>5</code></td>
|
|
<td>Sliding window (seconds) for the message-rate check</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:MaxDuplicateMessages</code></td>
|
|
<td><code>3</code></td>
|
|
<td>Identical messages in a row tolerated before rejection (E2E rooms are exempt — ciphertext differs each time)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:AutoMuteMinutes</code></td>
|
|
<td><code>5</code></td>
|
|
<td>Auto-mute duration once a user hits the violation threshold. <code>0</code> disables auto-mute (rejections still apply)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:ViolationThreshold</code></td>
|
|
<td><code>5</code></td>
|
|
<td>Rejected sends within <code>ViolationWindowMinutes</code> that trigger an auto-mute</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:ViolationWindowMinutes</code></td>
|
|
<td><code>5</code></td>
|
|
<td>Window (minutes) over which violations accumulate</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:MaxJoinsPerWindow</code></td>
|
|
<td><code>25</code></td>
|
|
<td>Max first-time channel joins per <code>JoinWindowSeconds</code> — keep this above your public channel count</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:JoinWindowSeconds</code></td>
|
|
<td><code>30</code></td>
|
|
<td>Sliding window (seconds) for the join-rate check</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:MaxChannelCreatesPerWindow</code></td>
|
|
<td><code>3</code></td>
|
|
<td>Max channel creations per <code>ChannelCreateWindowMinutes</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Spam:ChannelCreateWindowMinutes</code></td>
|
|
<td><code>10</code></td>
|
|
<td>Window (minutes) for the channel-create check</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="encryption">Encryption</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Encryption:Key</code></td>
|
|
<td><em>(auto-generated)</em></td>
|
|
<td>AES key for message encryption in transit</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Encryption:EncryptDatabase</code></td>
|
|
<td><code>false</code></td>
|
|
<td>Also encrypt message content at rest in SQLite</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="storage">Storage</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Storage:Path</code></td>
|
|
<td><em>(empty)</em></td>
|
|
<td>Directory for uploaded file blobs (attachments, avatars). Empty = <code>uploads/</code> in the app directory; the Docker image sets it to <code>/app/data/uploads</code> on the persistent volume</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Storage:CleanupIntervalHours</code></td>
|
|
<td><code>1</code></td>
|
|
<td>How often the cleanup job runs (hours)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Storage:RetentionDays</code></td>
|
|
<td><code>30</code></td>
|
|
<td>Days to keep uploaded files before cleanup</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="irc-gateway">IRC Gateway</h3>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Irc:Enabled</code></td>
|
|
<td><code>false</code></td>
|
|
<td>Enable the IRC protocol gateway</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:Port</code></td>
|
|
<td><code>6667</code></td>
|
|
<td>IRC plain-text listen port</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:TlsEnabled</code></td>
|
|
<td><code>false</code></td>
|
|
<td>Enable TLS termination for IRC</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:TlsPort</code></td>
|
|
<td><code>6697</code></td>
|
|
<td>IRC TLS listen port</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:TlsCertPath</code></td>
|
|
<td><em>(empty)</em></td>
|
|
<td>Path to a PKCS#12 (<code>.pfx</code>) certificate</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:TlsCertPassword</code></td>
|
|
<td><em>(empty)</em></td>
|
|
<td>Password for the certificate file</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:ServerName</code></td>
|
|
<td><code>echohub</code></td>
|
|
<td>IRC server name in protocol messages</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Irc:Motd</code></td>
|
|
<td><code>Welcome to EchoHub IRC Gateway!</code></td>
|
|
<td>Message of the day</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="server-logs-room">Server Logs Room</h3>
|
|
<p>When enabled, EchoHub auto-creates a read-only system channel and streams log events to it live,
|
|
so operators can watch the server from inside the app. Log lines are <strong>never stored as messages</strong> —
|
|
the rolling Serilog files remain the only persistence, and the room replays recent lines from those
|
|
files when someone opens it.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>ServerLogs:Enabled</code></td>
|
|
<td><code>true</code></td>
|
|
<td>Master switch for the live log room</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:RoomName</code></td>
|
|
<td><code>server-logs</code></td>
|
|
<td>Name of the auto-created channel (reserved — users can't create a channel with this name)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:MinRole</code></td>
|
|
<td><code>Mod</code></td>
|
|
<td>Minimum server role that can see and join the room (<code>Member</code>, <code>Mod</code>, <code>Admin</code>, <code>Owner</code>)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:MinLevel</code></td>
|
|
<td><code>Information</code></td>
|
|
<td>Minimum log level streamed to the room (<code>Verbose</code>, <code>Debug</code>, <code>Information</code>, <code>Warning</code>, <code>Error</code>, <code>Fatal</code>) — affects only the room, not the file/console sinks</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:BacklogLines</code></td>
|
|
<td><code>100</code></td>
|
|
<td>Recent log lines replayed from file when someone opens the room</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:LogDirectory</code></td>
|
|
<td><code>logs</code></td>
|
|
<td>Directory holding the rolling log files (must match the Serilog file sink path below)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>ServerLogs:LogFilePattern</code></td>
|
|
<td><code>echohub-server-*.log</code></td>
|
|
<td>Filename glob for the rolling log files inside <code>LogDirectory</code></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<h3 id="logging">Logging</h3>
|
|
<p>EchoHub uses <a href="https://serilog.net/">Serilog</a> for structured logging — console output + daily rolling files with 14-day retention by default.</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>Serilog:MinimumLevel:Default</code></td>
|
|
<td><code>Information</code></td>
|
|
<td>Global log level (<code>Debug</code>, <code>Information</code>, <code>Warning</code>, <code>Error</code>)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Serilog:MinimumLevel:Override:Microsoft</code></td>
|
|
<td><code>Warning</code></td>
|
|
<td>Suppress noisy framework logs</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Serilog:MinimumLevel:Override:Microsoft.AspNetCore</code></td>
|
|
<td><code>Warning</code></td>
|
|
<td>Suppress request pipeline logs</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Serilog:MinimumLevel:Override:Microsoft.EntityFrameworkCore</code></td>
|
|
<td><code>Warning</code></td>
|
|
<td>Suppress database query logs</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<p>Log files are written to <code>logs/echohub-server-YYYY-MM-DD.log</code>. To change the path or retention, edit the <code>Serilog:WriteTo</code> section in <code>appsettings.json</code>.</p>
|
|
<p>Want more verbose output for debugging? Set the minimum level to <code>Debug</code>:</p>
|
|
<pre><code class="lang-bash"># via environment variable
|
|
export Serilog__MinimumLevel__Default=Debug
|
|
|
|
# or command line
|
|
./EchoHub.Server --Serilog:MinimumLevel:Default=Debug
|
|
</code></pre>
|
|
|
|
</article>
|
|
|
|
<div class="contribution d-print-none">
|
|
<a href="https://github.com/HueByte/EchoHub/blob/master/docs/#L1" class="edit-link">Edit this page</a>
|
|
</div>
|
|
|
|
<div class="next-article d-print-none border-top" id="nextArticle"></div>
|
|
|
|
</div>
|
|
|
|
<div class="affix">
|
|
<nav id="affix"></nav>
|
|
</div>
|
|
</main>
|
|
|
|
<div class="container-xxl search-results" id="search-results"></div>
|
|
|
|
<footer class="border-top text-secondary">
|
|
<div class="container-xxl">
|
|
<div class="flex-fill">
|
|
<div class='footer-custom'><div class='footer-inner'><span class='footer-brand'>EchoHub</span><span class='footer-sep'>·</span><a href='https://github.com/HueByte/EchoHub'>GitHub</a><span class='footer-sep'>·</span><a href='https://echohub.voidcube.cloud'>Website</a></div></div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
</html>
|