<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Satish Patil</title>
    <description>The latest articles on DEV Community by Satish Patil (@satsvelke).</description>
    <link>https://dev.to/satsvelke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F345775%2Ff95cf8c3-1db5-462b-b19a-26384afe7283.jpg</url>
      <title>DEV Community: Satish Patil</title>
      <link>https://dev.to/satsvelke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/satsvelke"/>
    <language>en</language>
    <item>
      <title>🚀 RootAlert - Real-time Exception Tracking &amp; Alerting for .NET on Slack &amp; Teams</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Sat, 22 Mar 2025 13:54:26 +0000</pubDate>
      <link>https://dev.to/satsvelke/rootalert-real-time-exception-tracking-alerting-for-net-on-slack-teams-1edh</link>
      <guid>https://dev.to/satsvelke/rootalert-real-time-exception-tracking-alerting-for-net-on-slack-teams-1edh</guid>
      <description>&lt;p&gt;&lt;a href="https://www.nuget.org/packages/RootAlert/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fnuget%2Fv%2FRootAlert.svg" alt="NuGet Badge" width="86" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔍 &lt;strong&gt;Tired of missing critical exceptions in your .NET apps?&lt;/strong&gt; Meet &lt;strong&gt;RootAlert&lt;/strong&gt; – a lightweight, real-time error tracking library that captures unhandled exceptions, batches them intelligently, and alerts your team via &lt;strong&gt;Microsoft Teams&lt;/strong&gt; and &lt;strong&gt;Slack&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 Key Features
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Automatic exception handling&lt;/strong&gt; via middleware&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Real-time alerts&lt;/strong&gt; with batching to prevent spam&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Supports Microsoft Teams (Adaptive Cards) &amp;amp; Slack (Blocks &amp;amp; Sections)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Configurable batch interval using &lt;code&gt;TimeSpan&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Rich error logs including request details, headers, and stack traces&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Supports Redis &amp;amp; MSSQL for persistent storage&lt;/strong&gt;  &lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Installation
&lt;/h2&gt;

&lt;p&gt;Install RootAlert from &lt;strong&gt;NuGet&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet add package RootAlert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or via Package Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Install-Package RootAlert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Configure RootAlert in &lt;code&gt;Program.cs&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Storage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RootAlertOption&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-teams-webhook-url"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-slack-webhook-url"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRootAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootAlertSetting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandlingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRootAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Now, RootAlert will automatically capture all unhandled exceptions!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📡 Persistent Storage Options (Redis &amp;amp; MSSQL)
&lt;/h2&gt;

&lt;p&gt;RootAlert supports external storage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 &lt;strong&gt;RootAlert.Redis&lt;/strong&gt; → Stores logs in &lt;strong&gt;Redis&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🔹 &lt;strong&gt;RootAlert.MSSQL&lt;/strong&gt; → Stores logs in &lt;strong&gt;SQL Server&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🛠 Using Redis for Storage&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet add package RootAlert.Redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Redis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisAlertStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6379"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Ideal for distributed apps running on multiple servers!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🛠 Using MSSQL for Storage&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet add package RootAlert.MSSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.MSSQL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MSSQLAlertStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;SQL Table Schema for MSSQL&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;RootAlertLogs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ExceptionMessage&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;StackTrace&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;RequestUrl&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;HttpMethod&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Headers&lt;/span&gt; &lt;span class="n"&gt;NVARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="n"&gt;DATETIME2&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;GETUTCDATE&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔗 Microsoft Teams Integration
&lt;/h2&gt;

&lt;p&gt;RootAlert supports Microsoft Teams via:&lt;br&gt;
1️⃣ &lt;strong&gt;Incoming Webhooks (Connector)&lt;/strong&gt; - Simple &amp;amp; Quick Setup.&lt;br&gt;&lt;br&gt;
2️⃣ &lt;strong&gt;Microsoft Teams Workflow API&lt;/strong&gt; - Easier than Power Automate.&lt;/p&gt;

&lt;p&gt;📌 &lt;strong&gt;Steps to Configure Teams Webhook:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Microsoft Teams&lt;/strong&gt;, go to the desired channel.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;“…” (More options) → Connectors&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;“Incoming Webhook”&lt;/strong&gt;, configure it, and copy the &lt;strong&gt;Webhook URL&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add it to &lt;code&gt;RootAlertOptions&lt;/code&gt; in your &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📌 &lt;strong&gt;Using Microsoft Teams Workflow API&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Teams → &lt;strong&gt;“…” → Workflows&lt;/strong&gt; → Select &lt;strong&gt;"Post to a channel when a webhook request is received"&lt;/strong&gt; template.&lt;/li&gt;
&lt;li&gt;Set up the webhook &amp;amp; use the URL in RootAlert.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎥 Watch a step-by-step guide here:&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=jHTU_jUnswY" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rkrju9gdbbkyf0qwlv4.jpg" alt="Teams Workflow API Setup" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Slack Integration
&lt;/h2&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Go to &lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;Slack API&lt;/a&gt; &amp;amp; create a new Slack App&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
2️⃣ Enable &lt;strong&gt;Incoming Webhooks&lt;/strong&gt; under &lt;strong&gt;Features&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
3️⃣ Click &lt;strong&gt;"Add New Webhook to Workspace"&lt;/strong&gt; &amp;amp; select a channel.&lt;br&gt;&lt;br&gt;
4️⃣ Copy the &lt;strong&gt;Webhook URL&lt;/strong&gt; &amp;amp; use it in &lt;code&gt;RootAlertOptions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Structured Slack alerts using Blocks &amp;amp; Sections for clear error logs.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Example Batched Error Summary Alert
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 Root Alert - Batched Error Summary

🔴 Error #1
Error Count: 3
📅 Timestamp: 03/22/2025 6:30:29 PM
🌐 Request URL: /getuser
📡 HTTP Method: GET
📩 Headers: Accept: application/json
----------------------------------------------------
⚠️ Exception Details
❗ Type: HttpRequestException
💬 Message: Weather API failed to respond
----------------------------------------------------
🔍 Stack Trace
   at WeatherService.GetWeatherData() in WeatherService.cs:line 45
   at RootAlertMiddleware.Invoke(HttpContext context)
----------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;RootAlert is &lt;strong&gt;open-source&lt;/strong&gt; and designed for modern .NET applications to improve exception tracking and response time.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Give it a try &amp;amp; let me know your feedback!&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;NuGet:&lt;/strong&gt; &lt;a href="https://www.nuget.org/packages/RootAlert/" rel="noopener noreferrer"&gt;RootAlert&lt;/a&gt;&lt;br&gt;&lt;br&gt;
💻 &lt;strong&gt;GitHub Repo:&lt;/strong&gt; &lt;a href="https://github.com/satsvelke/RootAlert" rel="noopener noreferrer"&gt;RootAlert on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have questions? Drop them in the comments below! 😊&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>alerts</category>
      <category>microsoftteams</category>
      <category>slack</category>
    </item>
    <item>
      <title>RootAlert - Real-time Exception Tracking for .NET with Redis</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Thu, 13 Feb 2025 19:13:31 +0000</pubDate>
      <link>https://dev.to/satsvelke/-rootalert-real-time-exception-tracking-for-net-with-redis-3816</link>
      <guid>https://dev.to/satsvelke/-rootalert-real-time-exception-tracking-for-net-with-redis-3816</guid>
      <description>&lt;h1&gt;
  
  
  🚀 RootAlert - Real-time Exception Tracking for .NET
&lt;/h1&gt;

&lt;p&gt;RootAlert is an open-source &lt;strong&gt;real-time error tracking and alerting&lt;/strong&gt; library for .NET applications. It captures unhandled exceptions, batches them intelligently, and sends alerts to &lt;strong&gt;Microsoft Teams&lt;/strong&gt;, &lt;strong&gt;Slack&lt;/strong&gt;, and more.  &lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 Features
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Automatic exception tracking&lt;/strong&gt; with minimal setup&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Batched alerts&lt;/strong&gt; to prevent spam&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Microsoft Teams (Adaptive Cards) &amp;amp; Slack (Blocks &amp;amp; Sections)&lt;/strong&gt; support&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Customizable batch interval using &lt;code&gt;TimeSpan&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Supports Redis-based storage for persistence&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Rich error logs including request details, headers, and stack traces&lt;/strong&gt;  &lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Installation
&lt;/h2&gt;

&lt;p&gt;RootAlert is available on &lt;strong&gt;NuGet&lt;/strong&gt;. Install it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet add package RootAlert &lt;span class="nt"&gt;--version&lt;/span&gt; 0.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or via Package Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Install-Package RootAlert &lt;span class="nt"&gt;-Version&lt;/span&gt; 0.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Configure RootAlert in &lt;code&gt;Program.cs&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Register &lt;strong&gt;RootAlert&lt;/strong&gt; and configure your preferred alerting method (&lt;strong&gt;Teams, Slack, etc.&lt;/strong&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RootAlertOption&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-teams-webhook-url"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-slack-webhook-url"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisAlertStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6379"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Redis storage&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRootAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootAlertSetting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Handle exceptions first&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandlingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Then, log errors with RootAlert&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRootAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;RootAlert will now automatically capture all unhandled exceptions!&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Redis Storage for Persistent Logging
&lt;/h2&gt;

&lt;p&gt;By default, RootAlert batches errors in &lt;strong&gt;memory&lt;/strong&gt;, meaning errors are lost if the application restarts. To ensure persistence, you can use &lt;strong&gt;Redis&lt;/strong&gt; as storage.  &lt;/p&gt;

&lt;h3&gt;
  
  
  ** 1️⃣ Configure Redis Storage in &lt;code&gt;Program.cs&lt;/code&gt;**
&lt;/h3&gt;

&lt;p&gt;Update your &lt;strong&gt;RootAlertSetting&lt;/strong&gt; to use Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisAlertStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:6379"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Use Redis storage&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;2️⃣ Redis Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Persistence&lt;/strong&gt; - Logs are stored even after an app restart.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Scalability&lt;/strong&gt; - Works across multiple instances of your app.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Performance&lt;/strong&gt; - Faster retrieval and processing of batched errors.  &lt;/p&gt;


&lt;h2&gt;
  
  
  ⚠️ Important Notes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;❗ If an exception filter is added, RootAlert won't work.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Exception filters handle errors before middleware gets a chance to process them. Since RootAlert works as middleware, it will never see the exception if a filter catches it first.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Solution: Ensure RootAlert is added after any existing exception-handling middleware.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your application has a global exception-handling middleware, register RootAlert &lt;strong&gt;after&lt;/strong&gt; it to ensure exceptions are logged correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandlingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Your existing middleware&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRootAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Register RootAlert after the exception middleware&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏆 Microsoft Teams Integration
&lt;/h2&gt;

&lt;p&gt;RootAlert supports &lt;strong&gt;Microsoft Teams&lt;/strong&gt; via &lt;strong&gt;Adaptive Cards&lt;/strong&gt; for structured error logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 How to Get a Teams Webhook URL&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;RootAlert supports &lt;strong&gt;Microsoft Teams&lt;/strong&gt; integration via:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incoming Webhooks (Connector)&lt;/strong&gt; – Simple and quick setup. (Will be deprecated) &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft Teams Workflow API&lt;/strong&gt; – Easier than Power Automate, with a built-in Webhook template.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔹 Option 1: Using an Incoming Webhook (Connector)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This method is the easiest way to receive error alerts in a Teams channel.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;📌 Steps to Get a Teams Webhook URL&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Microsoft Teams&lt;/strong&gt; and go to the desired channel.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"…" (More options) → Connectors&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Find &lt;strong&gt;"Incoming Webhook"&lt;/strong&gt; and click &lt;strong&gt;"Configure"&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Name it &lt;strong&gt;RootAlert Notifications&lt;/strong&gt; and click &lt;strong&gt;Create&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Webhook URL&lt;/strong&gt; and use it in &lt;code&gt;RootAlertOptions&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔹 Option 2: Using Microsoft Teams Workflow API (via Webhook Template)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This method is even easier than Power Automate and uses a built-in workflow to receive data via Webhook.&lt;br&gt;
🎥 &lt;strong&gt;Watch this video for a step-by-step guide:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=jHTU_jUnswY" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rkrju9gdbbkyf0qwlv4.jpg" alt="Microsoft Teams Workflow API Setup" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=jHTU_jUnswY" rel="noopener noreferrer"&gt;YouTube Link: https://www.youtube.com/watch?v=jHTU_jUnswY&lt;/a&gt;&lt;/strong&gt;  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;📌 Steps to Configure Teams Workflow API&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Microsoft Teams and Go to Workflows&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;strong&gt;“…” (More options) → Workflows&lt;/strong&gt;.  --&amp;gt; Create &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select "Post to a channel when a webhook request is received" Template&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Search for &lt;strong&gt;"Post to a channel when a webhook request is received"&lt;/strong&gt; and select the &lt;strong&gt;ready-made template&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt; to proceed.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose Team and Channel&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Team&lt;/strong&gt; where you want to post alerts.
&lt;/li&gt;
&lt;li&gt;Choose the &lt;strong&gt;Channel&lt;/strong&gt; where notifications should appear.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Example Teams Alert (Adaptive Card)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;RootAlert sends alerts as &lt;strong&gt;rich Adaptive Cards&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Copy the Webhook URL&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;After selecting the Team and Channel, Teams will generate a &lt;strong&gt;Webhook URL&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Copy this URL and use it in your RootAlert settings.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2Fexample%2Fteams-card.png" alt="Teams Adaptive Card" width="800" height="400"&gt;
&lt;/h2&gt;


&lt;h2&gt;
  
  
  🚨 Example Error Alert
&lt;/h2&gt;

&lt;p&gt;RootAlert captures &lt;strong&gt;rich error details&lt;/strong&gt; including &lt;strong&gt;request details, headers, and stack traces&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🆔 Error ID: abc123
⏳ Timestamp: 02/05/2025 4:02:41 AM
----------------------------------------------------
🌐 REQUEST DETAILS
🔗 URL: /weatherforecast
📡 HTTP Method: GET
----------------------------------------------------
📩 REQUEST HEADERS
📝 User-Agent: Mozilla/5.0
----------------------------------------------------
⚠️ EXCEPTION DETAILS
❗ Type: DivideByZeroException
💬 Message: Attempted to divide by zero.
----------------------------------------------------
🔍 STACK TRACE
   at Program.Main() in Program.cs:line 54
   at RootAlertMiddleware.Invoke()
----------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠 Roadmap
&lt;/h2&gt;

&lt;p&gt;🔹 &lt;strong&gt;Database Storage&lt;/strong&gt; - Store logs in SQL, Redis, or NoSQL&lt;br&gt;&lt;br&gt;
🔹 &lt;strong&gt;Email Alerts&lt;/strong&gt; - Send exception reports via SMTP&lt;br&gt;&lt;br&gt;
🔹 &lt;strong&gt;Log Severity Filtering&lt;/strong&gt; - Send only critical errors  &lt;/p&gt;




&lt;h2&gt;
  
  
  📜 License
&lt;/h2&gt;

&lt;p&gt;RootAlert is open-source and available under the &lt;strong&gt;MIT License&lt;/strong&gt;.  &lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Contributing
&lt;/h2&gt;

&lt;p&gt;🚀 Contributions are welcome! Feel free to submit pull requests or feature requests on &lt;strong&gt;&lt;a href="https://github.com/satsvelke/RootAlert" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Connect with Us
&lt;/h2&gt;

&lt;p&gt;📧 &lt;strong&gt;Email:&lt;/strong&gt;  &lt;a href="mailto:satsvelke@gmail.com"&gt;satsvelke@gmail.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🐦 &lt;strong&gt;Twitter:&lt;/strong&gt; &lt;a href="https://twitter.com/satsvelke" rel="noopener noreferrer"&gt;@satsvelke&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>alerts</category>
      <category>dotnetcore</category>
      <category>redis</category>
      <category>exceptions</category>
    </item>
    <item>
      <title>RootAlert: Real-time Exception Tracking &amp; Alerts for .NET!</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Thu, 06 Feb 2025 19:18:44 +0000</pubDate>
      <link>https://dev.to/satsvelke/rootalert-real-time-exception-tracking-alerts-for-net-5a6f</link>
      <guid>https://dev.to/satsvelke/rootalert-real-time-exception-tracking-alerts-for-net-5a6f</guid>
      <description>&lt;h1&gt;
  
  
  🚀 RootAlert: Real-time Exception Tracking &amp;amp; Alerts for .NET!
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;RootAlert&lt;/strong&gt; is a powerful, lightweight &lt;strong&gt;real-time error tracking&lt;/strong&gt; and alerting library for .NET applications. Whether you're building a small API or a large-scale enterprise system, RootAlert helps you &lt;strong&gt;monitor unhandled exceptions and get instant alerts&lt;/strong&gt; in Microsoft Teams, Slack, and (soon) Email!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2Fexample%2Frootalert-banner.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2Fexample%2Frootalert-banner.png" alt="RootAlert" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 Why Use RootAlert?
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Automatically capture unhandled exceptions&lt;/strong&gt; via middleware.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Batch alerts to prevent spam&lt;/strong&gt; while still getting timely notifications.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Multi-service alerting&lt;/strong&gt; – Send error notifications to &lt;strong&gt;Teams, Slack, and Email&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Customizable batch intervals&lt;/strong&gt; – Choose when to receive grouped alerts.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Rich error logs&lt;/strong&gt; with &lt;strong&gt;request details, headers, and stack traces&lt;/strong&gt;.  &lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Installation &amp;amp; Setup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/RootAlert/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fnuget%2Fv%2FRootAlert.svg" alt="NuGet Badge" width="86" height="20"&gt;&lt;/a&gt;&lt;br&gt;
RootAlert is available on &lt;strong&gt;NuGet&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; dotnet add package RootAlert &lt;span class="nt"&gt;--version&lt;/span&gt; 0.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or via Package Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; Install-Package RootAlert &lt;span class="nt"&gt;-Version&lt;/span&gt; 0.1.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Add RootAlert to Your .NET Project&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;Program.cs&lt;/code&gt;, configure RootAlert with &lt;strong&gt;multiple alert destinations&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;RootAlert.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RootAlertOption&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-teams-webhook-url"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertOption&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AlertMethod&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AlertType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Slack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WebhookUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://your-slack-webhook-url"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootAlertSetting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;RootAlertSetting&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;BatchInterval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;RootAlertOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootAlertOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRootAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootAlertSetting&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Handle exceptions first&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandlingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Then, log errors with RootAlert&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRootAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Now, RootAlert will capture all unhandled exceptions and send alerts to multiple services!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Important Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;❗ If an exception filter is added, RootAlert won't work.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Reason:&lt;/strong&gt; Exception filters handle errors before middleware gets a chance to process them. Since RootAlert works as middleware, it will never see the exception if a filter catches it first.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Solution: Ensure RootAlert is added after any existing exception-handling middleware.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your application has a global exception-handling middleware, register RootAlert &lt;strong&gt;after&lt;/strong&gt; it to ensure exceptions are logged correctly. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExceptionHandlingMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Your existing middleware&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseRootAlert&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Register RootAlert after the exception middleware&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏆 Microsoft Teams Integration
&lt;/h2&gt;

&lt;p&gt;RootAlert supports &lt;strong&gt;Microsoft Teams&lt;/strong&gt; integration via:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Incoming Webhooks (Connector)&lt;/strong&gt; – Simple and quick setup. (Will be deprecated) &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microsoft Teams Workflow API&lt;/strong&gt; – Easier than Power Automate, with a built-in Webhook template.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔹 Option 1: Using an Incoming Webhook (Connector)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This method is the easiest way to receive error alerts in a Teams channel.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;📌 Steps to Get a Teams Webhook URL&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Microsoft Teams&lt;/strong&gt; and go to the desired channel.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;"…" (More options) → Connectors&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Find &lt;strong&gt;"Incoming Webhook"&lt;/strong&gt; and click &lt;strong&gt;"Configure"&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Name it &lt;strong&gt;RootAlert Notifications&lt;/strong&gt; and click &lt;strong&gt;Create&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Webhook URL&lt;/strong&gt; and use it in your RootAlert configuration.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;🔹 Option 2: Using Microsoft Teams Workflow API (via Webhook Template)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This method is even easier than Power Automate and uses a built-in workflow to receive data via Webhook.&lt;/p&gt;

&lt;p&gt;🎥 &lt;strong&gt;Watch this video for a step-by-step guide:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=jHTU_jUnswY" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rkrju9gdbbkyf0qwlv4.jpg" alt="Microsoft Teams Workflow API Setup" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=jHTU_jUnswY" rel="noopener noreferrer"&gt;YouTube Link: https://www.youtube.com/watch?v=jHTU_jUnswY&lt;/a&gt;&lt;/strong&gt;  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;📌 Steps to Configure Teams Workflow API&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open Microsoft Teams and Go to Workflows&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;strong&gt;“…” (More options) → Workflows&lt;/strong&gt;.  --&amp;gt; Create &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Select "Post to a channel when a webhook request is received" Template&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search for &lt;strong&gt;"Post to a channel when a webhook request is received"&lt;/strong&gt; and select the &lt;strong&gt;ready-made template&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt; to proceed.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose Team and Channel&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the &lt;strong&gt;Team&lt;/strong&gt; where you want to post alerts.
&lt;/li&gt;
&lt;li&gt;Choose the &lt;strong&gt;Channel&lt;/strong&gt; where notifications should appear.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Copy the Webhook URL&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After selecting the Team and Channel, Teams will generate a &lt;strong&gt;Webhook URL&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Copy this URL and use it in your RootAlert settings.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  💬 Slack Integration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 How to Set Up a Slack Webhook&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;&lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;https://api.slack.com/apps&lt;/a&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create a new Slack App → Enable &lt;strong&gt;Incoming Webhooks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add New Webhook to Workspace&lt;/strong&gt;, choose a channel.&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Webhook URL&lt;/strong&gt; and paste it in &lt;code&gt;RootAlertOptions&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Example Slack Alert (Blocks &amp;amp; Sections Format)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;RootAlert formats messages beautifully in Slack:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2Fexample%2Fslack-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2Fexample%2Fslack-message.png" alt="Slack Alert" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🚨 Example Error Alert
&lt;/h2&gt;

&lt;p&gt;This is how RootAlert captures errors and logs &lt;strong&gt;detailed request information&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🆔 Error ID: abc123
⏳ Timestamp: 02/05/2025 4:02:41 AM
----------------------------------------------------
🌐 REQUEST DETAILS
🔗 URL: /weatherforecast
📡 HTTP Method: GET
----------------------------------------------------
📩 REQUEST HEADERS
📝 User-Agent: Mozilla/5.0
----------------------------------------------------
⚠️ EXCEPTION DETAILS
❗ Type: DivideByZeroException
💬 Message: Attempted to divide by zero.
----------------------------------------------------
🔍 STACK TRACE
   at Program.Main() in Program.cs:line 54
   at RootAlertMiddleware.Invoke()
----------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔜 Coming Soon!
&lt;/h2&gt;

&lt;p&gt;✔ &lt;strong&gt;Database Storage&lt;/strong&gt; - Store logs in MSSQL and Postgres&lt;br&gt;
    For Redis -&amp;gt;&amp;gt; &lt;a href="https://dev.to/satsvelke/-rootalert-real-time-exception-tracking-for-net-with-redis-3816"&gt;using storage with Redis&lt;/a&gt;&lt;br&gt;
✔ &lt;strong&gt;Email Alerts via SMTP&lt;/strong&gt; - Get notifications in your inbox.&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Severity Filtering&lt;/strong&gt; - Customize alerts based on error level.  &lt;/p&gt;




</description>
      <category>dotnetcore</category>
      <category>monitoring</category>
      <category>errors</category>
      <category>alerts</category>
    </item>
    <item>
      <title>Implementing Distributed Caching with PostgreSQL in .NET: Sats.PostgresDistributedCache</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Tue, 28 Jan 2025 20:07:54 +0000</pubDate>
      <link>https://dev.to/satsvelke/implementing-distributed-caching-with-postgresql-in-net-satspostgresdistributedcache-3efp</link>
      <guid>https://dev.to/satsvelke/implementing-distributed-caching-with-postgresql-in-net-satspostgresdistributedcache-3efp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In modern applications, caching is essential to improve performance, reduce latency, and decrease the load on databases. Traditional in-memory caches like &lt;strong&gt;Redis&lt;/strong&gt; or &lt;strong&gt;Memcached&lt;/strong&gt; are widely used, but what if you already have PostgreSQL as your data store and want to leverage it for distributed caching as well? Introducing &lt;strong&gt;Sats.PostgresDistributedCache&lt;/strong&gt;, a custom distributed cache implementation that uses &lt;strong&gt;PostgreSQL&lt;/strong&gt; as the backing store.&lt;/p&gt;

&lt;p&gt;This article walks through how to set up &lt;strong&gt;Sats.PostgresDistributedCache&lt;/strong&gt;, a lightweight, high-performance distributed cache using PostgreSQL, and how to integrate it into your .NET application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Features of Sats.PostgresDistributedCache:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL as the Backing Store&lt;/strong&gt;: Store cache entries in a PostgreSQL database, utilizing the power and reliability of PostgreSQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration Support&lt;/strong&gt;: Cache entries can have expiration times, after which they will be automatically removed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic Caching Operations&lt;/strong&gt;: Set, get, remove, and refresh cache entries using simple methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with .NET&lt;/strong&gt;: Easy integration into your .NET application with minimal configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why Use PostgreSQL for Distributed Caching?
&lt;/h2&gt;

&lt;p&gt;Using &lt;strong&gt;PostgreSQL&lt;/strong&gt; for caching has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Familiarity&lt;/strong&gt;: If you already use PostgreSQL in your project, adding caching support doesn’t require new infrastructure or technology.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: PostgreSQL can scale vertically and horizontally, and using it for caching allows you to consolidate your data store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt;: Unlike in-memory caches, PostgreSQL ensures that cached data is stored persistently and can be recovered in case of server failures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Installation and Setup
&lt;/h2&gt;

&lt;p&gt;To get started, you’ll need to install the &lt;code&gt;Sats.PostgresDistributedCache&lt;/code&gt; NuGet package in your .NET project. You can do this by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Sats.PostgresDistributedCache &lt;span class="nt"&gt;--version&lt;/span&gt; 1.3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can use the &lt;strong&gt;NuGet Package Manager&lt;/strong&gt; in Visual Studio for a more graphical experience.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Sats.PostgresDistributedCache/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.shields.io%2Fnuget%2Fv%2FSats.PostgresDistributedCache.svg" alt="NuGet Badge" width="86" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the Cache in .NET
&lt;/h2&gt;

&lt;p&gt;After installing the package, add the following configuration in your &lt;code&gt;Startup.cs&lt;/code&gt; or &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Add PostgreSQL distributed cache with your connection string&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPostgresDistributedCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Host=myserver;Port=5432;Database=mydb;Username=myuser;Password=mypassword"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Add other services as needed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can inject the &lt;code&gt;IPostgreSqlDistributedCache&lt;/code&gt; interface into any class and use it for caching.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use the Cache
&lt;/h2&gt;

&lt;p&gt;Once configured, you can use the cache like any other distributed cache. Here is an example of setting and getting cache values using &lt;code&gt;Sats.PostgresDistributedCache&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IPostgreSqlDistributedCache&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MyService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IPostgreSqlDistributedCache&lt;/span&gt; &lt;span class="n"&gt;distributedCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_distributedCache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;distributedCache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetFromCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SetInCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RemoveFromCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;RefreshCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RefreshAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetStringFromCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SetStringInCacheAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_distributedCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache Table Schema
&lt;/h3&gt;

&lt;p&gt;The cache is stored in a PostgreSQL table named &lt;code&gt;Cache&lt;/code&gt;, which can be created with the following schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"Cache"&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"key"&lt;/span&gt; &lt;span class="nb"&gt;character&lt;/span&gt; &lt;span class="nb"&gt;varying&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"value"&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"expiration"&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="nv"&gt;"PK_Cache"&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="nv"&gt;"UQ_Cache_Key"&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Columns:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key&lt;/strong&gt;: A unique identifier for each cache entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt;: The cached data stored as text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expiration&lt;/strong&gt;: The expiration timestamp for each cache entry.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Supported .NET Versions
&lt;/h2&gt;

&lt;p&gt;This package supports the following .NET versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET 6.0&lt;/li&gt;
&lt;li&gt;.NET 7.0&lt;/li&gt;
&lt;li&gt;.NET 8.0&lt;/li&gt;
&lt;li&gt;.NET 9.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensure that your project is targeting one of these versions for compatibility with &lt;code&gt;Sats.PostgresDistributedCache&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Sats.PostgresDistributedCache&lt;/code&gt; package is licensed under the &lt;strong&gt;MIT License&lt;/strong&gt;, allowing you to freely use, modify, and distribute it in your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Sats.PostgresDistributedCache/" rel="noopener noreferrer"&gt;NuGet Package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/satsvelke/PostgresDistributedCache" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>distributedcaching</category>
      <category>postgressql</category>
      <category>dotnet</category>
      <category>caching</category>
    </item>
    <item>
      <title>.Net Core API Project with Embedded React.js UI</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Mon, 28 Oct 2024 19:08:08 +0000</pubDate>
      <link>https://dev.to/satsvelke/net-core-api-project-with-embedded-reactjs-ui-5763</link>
      <guid>https://dev.to/satsvelke/net-core-api-project-with-embedded-reactjs-ui-5763</guid>
      <description>&lt;p&gt;This is a sample .NET Core API project demonstrating how to serve an embedded React.js UI (built with Chakra UI) alongside API endpoints. The project shows how to integrate a front-end application as an embedded resource, allowing the UI to be accessed without a separate server.&lt;/p&gt;

&lt;p&gt;Features&lt;br&gt;
ASP.NET Core API for backend functionality.&lt;br&gt;
Embedded React.js UI built with Chakra UI or whatever ui framework, served as static files.&lt;br&gt;
Static File Handling with embedded resources, allowing seamless access to front-end assets.&lt;br&gt;
Setup Instructions&lt;br&gt;
Build the React App: Ensure the production build of your React app (located in the ui folder) is created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ui
npm install
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a build folder with the production-ready files. Move or rename this folder to ui, so it aligns with the project’s embedded resource setup.&lt;/p&gt;

&lt;p&gt;Configure the Project: Open the .csproj file and make sure the ui folder is set as an embedded resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;
    &amp;lt;EmbeddedResource Include="ui\**" /&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the Project: Go back to the project root and run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API and UI will be served on the specified port (default: &lt;a href="http://localhost:5244" rel="noopener noreferrer"&gt;http://localhost:5244&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Usage&lt;br&gt;
Access the React UI at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:5244/ui/index.html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Project Structure&lt;br&gt;
Program.cs: Configures API routes, static file handling, and embedded resources.&lt;br&gt;
&lt;code&gt;ui/&lt;/code&gt;: Contains the production build of the React.js app.&lt;br&gt;
&lt;code&gt;WeatherForecast.cs&lt;/code&gt;: Sample API endpoint to demonstrate data responses.&lt;br&gt;
URL Rewrite Handling&lt;br&gt;
The project uses a URL rewrite rule to handle references to assets directly. Requests for paths like &lt;code&gt;/assets/&lt;/code&gt;... are automatically rewritten to &lt;code&gt;/ui/assets/&lt;/code&gt;....&lt;/p&gt;

&lt;p&gt;GIT Url &lt;br&gt;
&lt;a href="https://github.com/satsvelke/embedded_resource_api_dotnet_react.git" rel="noopener noreferrer"&gt;https://github.com/satsvelke/embedded_resource_api_dotnet_react.git&lt;/a&gt;&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>resources</category>
      <category>netcore</category>
      <category>react</category>
    </item>
    <item>
      <title>HTMLtoPdf for .net core 3.1 - Converts HTML content to PDF using chrome executable</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Sat, 02 Oct 2021 06:44:44 +0000</pubDate>
      <link>https://dev.to/satsvelke/htmltopdf-for-net-core-3-1-converts-html-content-to-pdf-using-chrome-executable-56gm</link>
      <guid>https://dev.to/satsvelke/htmltopdf-for-net-core-3-1-converts-html-content-to-pdf-using-chrome-executable-56gm</guid>
      <description>&lt;p&gt;Note : Requires Chrome executable&lt;br&gt;
Nuget Link -&lt;a href="https://www.nuget.org/packages/Sats.Core.HTMLToPdf" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Sats.Core.HTMLToPdf&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          var url = @"d:\test.html";
          var chromePath = @"C:\Program Files\Google\Chrome\Application\chrome.exe";

    var output = new ChromeOptions().AddOptions(b =&amp;gt;
                        {
                            b.Headless();
                            b.DisableGPU();
                            b.WithoutHeader();

                        }).ToPdf(new ChromeDetails()
                        {
                            ChromePath = chromePath,
                            HtmlPath = url,
                            DeleteOutputFile = true, //optional
                           // OutputPath = @"d:\print.pdf" // (add if Environment.CurrentDirectory does not have access rights)
                        });


    File.WriteAllBytes(@"d:\print.pdf", output.FileDetails.File);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>pdf</category>
      <category>netcore</category>
      <category>htmltopdf</category>
      <category>chrome</category>
    </item>
    <item>
      <title>HTMLtoPdf - Converts HTML content to PDF using chrome executable</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Mon, 27 Sep 2021 05:28:17 +0000</pubDate>
      <link>https://dev.to/satsvelke/htmltopdf-converts-html-content-to-pdf-using-chrome-executable-3lmf</link>
      <guid>https://dev.to/satsvelke/htmltopdf-converts-html-content-to-pdf-using-chrome-executable-3lmf</guid>
      <description>&lt;h1&gt;
  
  
  Note : Requires Chrome executable
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Nuget Link - &lt;a href="https://www.nuget.org/packages/Sats.HTMLtoPdf" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/Sats.HTMLtoPdf&lt;/a&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Github link - &lt;a href="https://github.com/satsvelke/HTMLtoPdf" rel="noopener noreferrer"&gt;https://github.com/satsvelke/HTMLtoPdf&lt;/a&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;           var url = @"d:\Vaccination.html";
          var chromePath = @"C:\Program Files\Google\Chrome\Application\chrome.exe";

          // returns byte array of file 
          var pdf = new ChromeOptions().AddOptions(b =&amp;gt;
                                      {
                                          b.Headless();
                                          b.DisableGPU();
                                          b.WithoutHeader();

                                      }).Pdf(new ChromeDetails() { ChromePath = chromePath, HtmlPath = url
 OutputPath = @"d:\print.pdf" // optional (add if 
             Environment.CurrentDirectory does not have access rights)
         });

           File.WriteAllBytes(@"d:\print.pdf", pdf);  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Version : 2.1 &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        var url = @"d:\test.html";
        var chromePath = @"C:\Program Files\Google\Chrome\Application\chrome.exe";

        var output = new ChromeOptions().AddOptions(b =&amp;gt;
                            {
                                b.Headless();
                                b.DisableGPU();
                                b.WithoutHeader();

                            }).ToPdf(new ChromeDetails()
                            {
                                ChromePath = chromePath,
                                HtmlPath = url,
                                DeleteOutputFile = true, //optional
                               // OutputPath = @"d:\print.pdf" // (add if Environment.CurrentDirectory does not have access rights)
                            });


        File.WriteAllBytes(@"d:\print.pdf", output.FileDetails.File);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;output.FileDetails.File will have byte array of created pdf&lt;br&gt;
output.ProcessDetails will have output details like errors&lt;/p&gt;

</description>
      <category>html</category>
      <category>pdf</category>
      <category>csharp</category>
    </item>
    <item>
      <title>kick start project in .net 5</title>
      <dc:creator>Satish Patil</dc:creator>
      <pubDate>Sun, 22 Nov 2020 07:38:18 +0000</pubDate>
      <link>https://dev.to/satsvelke/kick-start-project-in-net-5-hm1</link>
      <guid>https://dev.to/satsvelke/kick-start-project-in-net-5-hm1</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/satsvelke/Net-Core-API" rel="noopener noreferrer"&gt;https://github.com/satsvelke/Net-Core-API&lt;/a&gt;&lt;/p&gt;

</description>
      <category>net5</category>
    </item>
  </channel>
</rss>
