webhook sink for litty-logs. yeets litty-fied logs straight to Matrix chat rooms via hookshot webhooks. your critical errors land in the group chat formatted all nice instead of rotting in a log file no cap ๐ฅ
$ dotnet add package LittyLogs.Webhooksyo your .NET logs are giving corporate dystopia energy rn and thats not it bestie. litty-logs fully rewrites all them boring built-in framework messages into gen alpha slang while also blessing your terminal with emojis and ANSI colors no cap
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
[๐ฅ info] [2026-02-18T21:45:00.420Z] [Lifetime] we vibing on http://localhost:5000 fr fr ๐ง
[๐ฅ info] [2026-02-18T21:45:00.421Z] [Lifetime] app is bussin and ready to slay bestie ๐
yeet Ctrl+C to dip out no cap
[๐ฅ info] [2026-02-18T21:45:00.421Z] [Lifetime] content root living at /app bestie ๐
dotnet add package LittyLogs
# for xUnit test output (optional, separate package)
dotnet add package LittyLogs.Xunit
# for file sink with rotation and gzip compression (optional, separate package)
dotnet add package LittyLogs.File
# for webhook sink โ yeet logs to Matrix, Teams, etc (optional, separate package)
dotnet add package LittyLogs.Webhooks
# for the CLI tool that litty-fies build, test, publish, pack, and clean output
dotnet tool install --global LittyLogs.Tool
using LittyLogs;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddLittyLogs(); // thats it bestie ๐ฅ
var app = builder.Build();
app.Run();
using LittyLogs;
var host = Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging => logging.AddLittyLogs())
.ConfigureServices(services => services.AddHostedService<MyService>())
.Build();
await host.RunAsync();
using LittyLogs;
using Microsoft.Extensions.Logging;
using var factory = LoggerFactory.Create(logging =>
{
logging.SetMinimumLevel(LogLevel.Trace);
logging.AddLittyLogs();
});
var logger = factory.CreateLogger("MyScript");
logger.LogInformation("we in here bestie ๐ฅ");
using LittyLogs.Xunit;
using Xunit;
using Xunit.Abstractions;
public class MyTests
{
private readonly ILogger<MyTests> _logger;
public MyTests(ITestOutputHelper output)
{
// one line to litty-fy your test output bestie ๐
_logger = output.CreateLittyLogger<MyTests>();
}
[Fact]
public void MyTest()
{
_logger.LogInformation("this shows up litty-fied in test output ๐ฅ");
}
}
same litty rewrites and emojis, but as valid JSON. your log aggregator is gonna love this no cap
using LittyLogs;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddLittyJsonLogs(); // structured JSON with emojis bestie ๐ฅ
var app = builder.Build();
app.Run();
output:
{"timestamp":"2026-02-19T10:45:00.420Z","level":"info","emoji":"๐ฅ","category":"Lifetime","message":"app is bussin and ready to slay bestie ๐
yeet Ctrl+C to dip out no cap"}
emojis in JSON? absolutely bussin โ JSON is UTF-8 native so every parser on earth handles it perfectly ๐
using LittyLogs.File;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddLittyFileLogs(opts =>
{
opts.FilePath = "logs/app.log";
opts.OutputFormat = LittyFileOutputFormat.Text; // or Json for structured output
opts.RollingInterval = LittyRollingInterval.Daily;
opts.MaxFileSizeBytes = 10 * 1024 * 1024; // 10MB then rotate
opts.CompressionMode = LittyCompressionMode.Gzip; // compress rotated files ๐๏ธ
});
var app = builder.Build();
app.Run();
features that go hard:
Channel<string> based, your app thread never blocks on disk writes ๐.gz, active file stays uncompressedcritical error hits? your group chat knows about it instantly, formatted all nice with emojis
using LittyLogs.Webhooks;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddLittyMatrixLogs("https://hookshot.example.com/webhook/abc123"); // one liner bestie ๐ฃ
var app = builder.Build();
app.Run();
with full options:
builder.Logging.AddLittyMatrixLogs("https://hookshot.example.com/webhook/abc123", opts =>
{
opts.MinimumLevel = LogLevel.Warning; // only Warning+ goes to chat (default)
opts.Username = "LittyLogs"; // bot display name in chat
opts.BatchSize = 10; // max messages per batch
opts.BatchInterval = TimeSpan.FromSeconds(2); // flush interval
});
features that go hard:
Channel<T> based, groups messages by interval (2s) or count (10), your app thread never blocks ๐Microsoft.Extensions.Http.Resilience ๐Warning so your chat dont get spammed with trace logs ๐"LittyWebhooks" for custom configbuilder.Logging.AddLittyTeamsLogs("https://your-org.webhook.office.com/webhook/..."); // one liner bestie ๐ฆ
with full options:
builder.Logging.AddLittyTeamsLogs("https://your-org.webhook.office.com/webhook/...", opts =>
{
opts.MinimumLevel = LogLevel.Warning; // only Warning+ goes to chat (default)
opts.Username = "LittyLogs"; // shows in the card header
opts.BatchSize = 10; // max messages per batch
opts.BatchInterval = TimeSpan.FromSeconds(2); // flush interval
});
Teams-specific features that go hard:
all the boring framework messages you see every dotnet run:
| boring version ๐ | litty version ๐ฅ |
|---|---|
| Application started. Press Ctrl+C to shut down. | app is bussin and ready to slay bestie ๐ yeet Ctrl+C to dip out no cap |
| Now listening on: {url} | we vibing on {url} fr fr ๐ง |
| Content root path: {path} | content root living at {path} bestie ๐ |
| Hosting environment: {env} | we in our {env} era rn โจ |
| Application is shutting down... | app said aight imma head out ๐ |
| Request starting {details} | yo a request just slid in: {details} ๐ |
| Request finished {details} | request finished cooking: {details} ๐ณ |
plus hosting lifecycle, endpoint routing, and more fr fr
| level | emoji | vibe |
|---|---|---|
| Trace | ๐ | lowkey peeking |
| Debug | ๐ | investigating bestie |
| Information | ๐ฅ | bussin as usual |
| Warning | ๐ค | not it |
| Error | ๐ | big L |
| Critical | โ ๏ธ | its giving death |
builder.Logging.AddLittyLogs(options =>
{
options.RewriteMessages = true; // rewrite framework messages (default: true, thats the whole point)
options.UseColors = true; // ANSI colors (default: true)
options.ShortenCategories = true; // yeet namespace bloat (default: true)
options.UseUtcTimestamp = true; // UTC timestamps (default: true, international rizz)
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffK"; // ISO 8601 with milliseconds (default)
options.TimestampFirst = false; // false = RFC 5424 (level first), true = observability style (timestamp first)
});
dotnet litty CLI tool โ litty-fy your build, test, publish, pack, and clean output ๐งชyour app logs are litty but dotnet build, dotnet test, dotnet publish, dotnet pack, and dotnet clean output is still giving corporate energy? install the tool and never look at boring terminal output again no cap
# install the tool
dotnet tool install --global LittyLogs.Tool
# litty-fy your test output (auto-shows ITestOutputHelper output too)
dotnet litty test
# litty-fy your build output
dotnet litty build
# litty-fy your publish output
dotnet litty publish
# litty-fy your pack output โ nupkgs go brrr ๐ฆ
dotnet litty pack
# litty-fy your clean output โ watch artifacts get yeeted in style ๐๏ธ
dotnet litty clean
# all args pass through to the underlying dotnet command
dotnet litty test --filter "FullyQualifiedName~MyTests"
dotnet litty build -c Release
dotnet litty publish -c Release --self-contained
dotnet litty pack -c Release
dotnet litty clean -c Release
Passed! - Failed: 0, Passed: 66, Skipped: 0, Total: 66, Duration: 80 ms - LittyLogs.Tests.dll (net10.0)
[xUnit.net] scouting for tests in LittyLogs.Tests ๐
[xUnit.net] found the squad in LittyLogs.Tests ๐
[xUnit.net] lets gooo LittyLogs.Tests is cooking ๐ฅ
โ
LittyLogs.Tests.MyTest.SomeTest [26 ms]
[xUnit.net] LittyLogs.Tests absolutely ate no crumbs ๐
all tests ate and left no crumbs ๐
total vibes checked: 66
ate: 66 โ
cooked in 0.5 Seconds โฑ๏ธ
litty-logs only rewrites known framework messages. your custom log messages pass through with the bussin formatting (emojis, colors, short categories) but the actual message text stays exactly how you wrote it no cap
logger.LogInformation("my custom message stays exactly like this");
// output: [๐ฅ info] [2026-02-18T21:45:00.420Z] [MyService] my custom message stays exactly like this
seven example projects in examples/ so you can see litty-logs in every scenario:
| example | what it shows | run it |
|---|---|---|
WebApi | startup demo with level-first โ timestamp-first โ JSON, then server runs | just example web |
HostedService | startup demo with both timestamp modes, then background service vibes | just example hosted |
Console | side-by-side text + JSON output comparison | just example console |
Xunit | litty-fied xUnit test output with all log levels + TimestampFirst test | just example xunit |
Json | structured JSON logging with both timestamp configs | just example json |
FileSink | file sink with level-first โ timestamp-first โ JSON, reads em all back | just example filesink |
Webhooks | dual webhook sink (Matrix + Teams) with mock listeners or live endpoints | just example webhooks |
every example auto-showcases ALL the modes when you run it โ no hidden flags, no secret handshakes. you run it, you see everything ๐
the webhooks example runs three demos: Matrix-only, Teams-only, and dual mode (both firing simultaneously). set HOOKSHOT_URL and/or TEAMS_WEBHOOK_URL in .env to go live โ any sink without a URL falls back to a local mock listener so it always works bestie ๐ช๐ฅ
this project uses just as the task runner. here are the vibes:
| recipe | what it does |
|---|---|
just build | build the whole solution with litty-fied output ๐๏ธ๐ฅ |
just test | run all tests with litty-fied output ๐งช๐ฅ |
just publish | publish with litty-fied output ๐ค๐ฅ |
just pack | pack all five NuGet packages with litty-fied output ๐ฆ๐ฅ |
just clean | yeet all build artifacts with litty-fied output ๐๏ธ๐ฅ |
just bump patch | bump the patch version (also: minor, major) |
just bump-pre dev.1 | slap a pre-release label on (e.g. 0.1.0-dev.1) |
just release patch | full gitflow release โ bump, branch, finish, push ๐ |
just release-current | gitflow release without bumping (for first release etc.) |
just re-release | nuke old releases + tags everywhere, re-do the current version ๐ |
just release-dev patch | dev/pre-release โ bump + label + ship (e.g. 0.1.1-dev) ๐งช |
just hotfix patch | start a gitflow hotfix branch off main ๐ |
just finish | finish whatever gitflow branch youre on (hotfix/release/support) + push ๐ |
just nuget-push | manually push packages to nuget.org |
just example <name> | run an example โ web, hosted, console, xunit, json, filesink, webhooks ๐ฅ |
just setup-completions | install shell tab-completions for just example <tab> |
tab-complete just example <tab> to see all available examples. works with zsh and bash:
# auto-install to your shell rc file
just setup-completions
# or source manually
source completions/just.zsh # zsh
source completions/just.bash # bash
version lives in one place: Directory.Build.props. all five packages inherit from it. we use gitflow via the git flow CLI โ main is production, develop is the integration branch, releases and hotfixes get their own branches ๐ฅ
# from develop โ full gitflow ceremony (bump, branch, finish, push โ all in one command)
just release patch # 0.1.0 โ 0.1.1, pushes everything, pipeline goes brrr
just release minor # 0.1.0 โ 0.2.0
just release major # 0.1.0 โ 1.0.0
# dev/pre-release for testing the pipeline
just release-dev patch # 0.1.0 โ 0.1.1-dev
just release-dev minor beta.1 # 0.1.0 โ 0.2.0-beta.1
# release the current version without bumping (e.g. first release)
just release-current
all release commands auto-push develop + main + tag to origin when done. no manual git push needed fr fr ๐ฅ
# from main โ start a hotfix when something is bricked in prod
just hotfix patch
# make your fix, commit it, then finish + push
just finish
just finish auto-detects if youre on a hotfix, release, or support branch, does git flow finish, and pushes everything. one command to rule them all ๐
forgejo actions on a self-hosted runner handles the whole squad:
ci.yml) โ builds, tests (with litty output ๐ฅ), and packs on every push/PR to develop and main. if this fails your code is bricked and you should not merge no caprelease.yml) โ triggered by v* tags. the full pipeline hits THREE destinations:
.nupkg files with --skip-duplicate so retries dont catch Ls.nupkg assets attached ๐ gh CLI with .nupkg assets on the mirror repo ๐pipeline features that go hard:
- (like 0.1.0-dev, 1.0.0-beta.1) auto-flag as pre-release on both platforms ๐งชCHANGELOG.md for that professional rizz ๐Directory.Build.props or the pipeline tells you its not it ๐see docs/runner-setup.md for runner setup and required secrets no cap
stuff that would go absolutely crazy but aint started yet. vibes only rn no cap
wanna see one of these happen? PRs are open bestie, or just vibe in the issues ๐
litty-logs takes security seriously even though we dont take ourselves seriously no cap. heres the tldr:
http/https schemes allowedfull details in docs/security.md
found a vulnerability? dont yeet it in a public issue โ open a security advisory instead bestie ๐
MIT โ share the vibes bestie โ๏ธ