Last active
December 9, 2025 13:32
-
-
Save pedroinfo/126fe2597253d7e087af1ff93f35210d to your computer and use it in GitHub Desktop.
WebFormsOTELLogger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| LogService.LogBusiness( | |
| action: "ButtonClicked", | |
| page: "/Clientes.aspx", | |
| userId: "123", | |
| entityId: "456", | |
| details: "User clicked the SAVE button" | |
| ); | |
| LogService.LogBusiness( | |
| action: "PageOpened", | |
| page: "/Pedidos.aspx", | |
| userId: "789", | |
| details: "User opened the orders page" | |
| ); | |
| try | |
| { | |
| throw new Exception("Test exception"); | |
| } | |
| catch (Exception ex) | |
| { | |
| LogService.LogError(ex); | |
| } | |
| Output | |
| {"timestamp":"2025-12-09T15:30:12.345Z","severity_text":"Info","severity_number":9,"body":"ButtonClicked","attributes":{"eventType":"BUSINESS","action":"ButtonClicked","page":"/Clientes.aspx","userId":"123","entityId":"456","details":"User clicked the SAVE button"}} | |
| {"timestamp":"2025-12-09T15:31:00.000Z","severity_text":"Info","severity_number":9,"body":"OK","attributes":{"eventType":"HEARTBEAT"}} | |
| {"timestamp":"2025-12-09T15:32:11.456Z","severity_text":"Error","severity_number":17,"body":"Test exception","attributes":{"eventType":"UnhandledException","details":"stacktrace here"}} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| public class Global : System.Web.HttpApplication | |
| { | |
| protected void Application_Start(object sender, EventArgs e) | |
| { | |
| HeartbeatService.Start(); | |
| } | |
| protected void Application_Error(object sender, EventArgs e) | |
| { | |
| Exception ex = Server.GetLastError(); | |
| LogService.LogError(ex); | |
| Server.ClearError(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Data.SqlClient; | |
| using System.Timers; | |
| namespace WebFormsOTELLogger.Logging | |
| { | |
| public static class HeartbeatService | |
| { | |
| private static Timer _heartbeatTimer; | |
| private static DateTime _applicationStartTime = DateTime.UtcNow; | |
| private static string _connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString; | |
| private static int _heartbeatIntervalSeconds; | |
| private static bool _initialized = false; | |
| // -------------------------------------------------------------------- | |
| // Public method to explicitly start heartbeat | |
| // -------------------------------------------------------------------- | |
| public static void Start() | |
| { | |
| if (_initialized) return; // prevent multiple starts | |
| int.TryParse(System.Configuration.ConfigurationManager.AppSettings["HeartbeatIntervalSeconds"], out _heartbeatIntervalSeconds); | |
| if (_heartbeatIntervalSeconds <= 0) _heartbeatIntervalSeconds = 60; | |
| _heartbeatTimer = new Timer(_heartbeatIntervalSeconds * 1000); | |
| _heartbeatTimer.AutoReset = true; | |
| _heartbeatTimer.Elapsed += (s, e) => SendHeartbeat(); | |
| _heartbeatTimer.Start(); | |
| _initialized = true; | |
| } | |
| // -------------------------------------------------------------------- | |
| // Internal method to send heartbeat | |
| // -------------------------------------------------------------------- | |
| private static void SendHeartbeat() | |
| { | |
| bool dbHealthy = false; | |
| bool appAlive = true; // basic health: heartbeat running | |
| try | |
| { | |
| using (var conn = new SqlConnection(_connectionString)) | |
| { | |
| conn.Open(); | |
| dbHealthy = true; | |
| } | |
| } | |
| catch | |
| { | |
| dbHealthy = false; | |
| } | |
| var uptime = DateTime.UtcNow - _applicationStartTime; | |
| var attributes = new | |
| { | |
| eventType = "HEARTBEAT", | |
| serviceName = "WebFormsOTELLogger", | |
| environment = "prod", | |
| instanceId = Environment.MachineName, | |
| uptimeSeconds = uptime.TotalSeconds, | |
| dbHealthy, | |
| appAlive, | |
| version = "1.0.0" | |
| }; | |
| LogService.WriteOtelLogPublic(OTelSeverity.Info, "Heartbeat", attributes); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Web; | |
| using System.Web.Script.Serialization; | |
| namespace WebFormsOTELLogger.Logging | |
| { | |
| public static class LogService | |
| { | |
| private static JavaScriptSerializer _json = new JavaScriptSerializer(); | |
| private static string _logDirectory; | |
| private static string _archiveDirectory; | |
| private static int _logRetentionDays; | |
| private static bool _initialized = false; | |
| // -------------------------------------------------------------------- | |
| // Public method to initialize log service | |
| // -------------------------------------------------------------------- | |
| public static void Start() | |
| { | |
| if (_initialized) return; | |
| _logDirectory = HttpContext.Current.Server.MapPath( | |
| System.Configuration.ConfigurationManager.AppSettings["LogDirectory"] | |
| ); | |
| _archiveDirectory = Path.Combine(_logDirectory, "archive"); | |
| if (!int.TryParse(System.Configuration.ConfigurationManager.AppSettings["LogRetentionDays"], out _logRetentionDays)) | |
| _logRetentionDays = 90; | |
| Directory.CreateDirectory(_logDirectory); | |
| Directory.CreateDirectory(_archiveDirectory); | |
| CleanupOldLogs(); | |
| _initialized = true; | |
| } | |
| // -------------------------------------------------------------------- | |
| // Log an unhandled exception | |
| // -------------------------------------------------------------------- | |
| public static void LogError(Exception ex) | |
| { | |
| var attributes = new | |
| { | |
| eventType = "UnhandledException", | |
| details = ex.ToString() | |
| }; | |
| WriteOtelLog(OTelSeverity.Error, ex.Message, attributes); | |
| } | |
| // -------------------------------------------------------------------- | |
| // Log a business event | |
| // -------------------------------------------------------------------- | |
| public static void LogBusiness(string action, string page = "", string userId = "", string entityId = "", string details = "") | |
| { | |
| var attributes = new | |
| { | |
| eventType = "BUSINESS", | |
| action, | |
| page, | |
| userId, | |
| entityId, | |
| details | |
| }; | |
| WriteOtelLog(OTelSeverity.Info, action, attributes); | |
| } | |
| // -------------------------------------------------------------------- | |
| // Public method for HeartbeatService to write OTEL log | |
| // -------------------------------------------------------------------- | |
| public static void WriteOtelLogPublic(OTelSeverity severity, string body, object attributes) | |
| { | |
| WriteOtelLog(severity, body, attributes); | |
| } | |
| // -------------------------------------------------------------------- | |
| // Internal method to write OTEL JSON log with daily rotation | |
| // -------------------------------------------------------------------- | |
| private static void WriteOtelLog(OTelSeverity severity, string body, object attributes) | |
| { | |
| if (!_initialized) return; | |
| try | |
| { | |
| var logObj = new | |
| { | |
| timestamp = DateTime.UtcNow.ToString("o"), | |
| severity_text = severity.ToSeverityText(), | |
| severity_number = (int)severity, | |
| body, | |
| attributes | |
| }; | |
| string currentFile = Path.Combine(_logDirectory, "current.log"); | |
| File.AppendAllText(currentFile, _json.Serialize(logObj) + Environment.NewLine); | |
| RotateDaily(currentFile); | |
| } | |
| catch | |
| { | |
| // Prevent logging errors from breaking the application | |
| } | |
| } | |
| // -------------------------------------------------------------------- | |
| // Rotate logs daily into archive folder | |
| // -------------------------------------------------------------------- | |
| private static void RotateDaily(string currentFile) | |
| { | |
| if (!File.Exists(currentFile)) return; | |
| DateTime lastWrite = File.GetLastWriteTime(currentFile); | |
| if (lastWrite.Date < DateTime.Now.Date) | |
| { | |
| string archiveFile = Path.Combine(_archiveDirectory, lastWrite.ToString("yyyy-MM-dd") + ".log"); | |
| if (File.Exists(archiveFile)) | |
| File.Delete(archiveFile); | |
| File.Move(currentFile, archiveFile); | |
| } | |
| } | |
| // -------------------------------------------------------------------- | |
| // Cleanup logs older than configured retention days | |
| // -------------------------------------------------------------------- | |
| private static void CleanupOldLogs() | |
| { | |
| foreach (var file in Directory.GetFiles(_archiveDirectory, "*.log")) | |
| { | |
| if (File.GetLastWriteTime(file) < DateTime.Now.AddDays(-_logRetentionDays)) | |
| File.Delete(file); | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // OTEL severity levels (main levels only) | |
| public enum OTelSeverity | |
| { | |
| Trace = 1, | |
| Debug = 5, | |
| Info = 9, | |
| Warn = 13, | |
| Error = 17, | |
| Fatal = 21 | |
| } | |
| // Extension method to map severity enum to text | |
| public static class OTelSeverityExtensions | |
| { | |
| public static string ToSeverityText(this OTelSeverity sev) | |
| { | |
| switch (sev) | |
| { | |
| case OTelSeverity.Trace: return "TRACE"; | |
| case OTelSeverity.Debug: return "DEBUG"; | |
| case OTelSeverity.Info: return "INFO"; | |
| case OTelSeverity.Warn: return "WARN"; | |
| case OTelSeverity.Error: return "ERROR"; | |
| case OTelSeverity.Fatal: return "FATAL"; | |
| default: return "INFO"; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <configuration> | |
| <appSettings> | |
| <!-- Log directory (relative or absolute path) --> | |
| <add key="LogDirectory" value="~/App_Data/Logs" /> | |
| <!-- Heartbeat interval in seconds --> | |
| <add key="HeartbeatIntervalSeconds" value="60" /> | |
| <!-- Number of days to keep archived logs --> | |
| <add key="LogRetentionDays" value="90" /> | |
| </appSettings> | |
| </configuration> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment