It is surprising frequent that I have customers who do not like using third party libraries. They want to own all the code. As a result, I have my own little custom logging system that I generally implement in my C# apps that need event logging. Each implementation is a little different so I will make a generic one here. Lets dig into building a simple custom logging setup in C#.
Custom Logging Database
First off we need an database table to store our events. I am using a database called SPD, so change that to what you need it to be. This script will create the table for you.
USE [SPD] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[EventLog]( [EventLogID] [int] IDENTITY(1,1) NOT NULL, [Event] [varchar](255) NULL, [EventText] [varchar](max) NULL, [EventDate] [datetime] NULL, CONSTRAINT [PK_EventLog] PRIMARY KEY CLUSTERED ( [EventLogID] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO ALTER TABLE [dbo].[EventLog] ADD CONSTRAINT [DF_EventLog_EventDate] DEFAULT (getdate()) FOR [EventDate] GO
This script creates an EventLog table. We have a auto-incrementing EventLogID integer. Next, there is an Event and EventText, one is where I put my custom messages and the other is for system messages. Finally there is an EventDate which automatically fills in with the current data and time.
With the table handled. We need to stored procedure.
USE [SPD] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: Ryan -- Description: Captures Events and Errors here -- ============================================= CREATE PROCEDURE [dbo].[LogEvent] @Event AS VARCHAR(255), @EventText AS VARCHAR(MAX) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; INSERT INTO [dbo].[EventLog] ([Event] ,[EventText]) VALUES (@Event ,@EventText) END
Custom Logging Application in C#
With the database all made up. We can look to the application. I am building this in a Windows Forms application but you can use this in any of the project types.
Entity Framework
After you get your project spun up, it’s time to create a Entity Framework Connection to the SPD or whatever database. You do this by finding the ADO.NET Entity Data Model in the data subsection of Visual C# stuff when you add a new item to the project. I tend to name mine after the project because this logger is just another stored procedure of a bigger database. Here I will call it SPD Model.
You want to select EF Designer from database.
Setup your connection through the New Connection button and fill out the connection properties window.
It will look something like this when finished.
I use 6.x.
Next we want to select our LogEvent stored procedure and press the finish button.
In your Solution Explorer you should have a new set of objects called SPDModel.*. The .* is a wildcard for the extensions for the pre-DOS kids.
EventLog Class
Now we have the database all setup and wired into the application, lets actually call it. We will need an object to work with here so I create a new class and call it EventLog. I will make it static since we don’t really want to create an object here.
using System; namespace LoggingExample { public static class EventLog { public static void writeEvent(string eventTitle, string eventText) { if (eventTitle.Length > 254) { eventTitle = eventTitle.Substring(0, 254); } try { using (SPDEntities pe = new SPDEntities()) { pe.LogEvent(eventTitle, eventText); } } catch (Exception ex) { throw new Exception("Event writing has failed."); } } } }
You will likely get a couple of errors copying this. You need to match the namespace to your project and make sure the SPDEntities is named likely *Entities where * is what you called your *Model in the step before.
Anyway, we do a quick check to make sure the title, which has a database limit is acceptable, I just truncate the message if it gets too long. The other parameter is a VARCHAR(MAX) field and a C# string both can store up to 2,147,483,647 characters so no overflow. If you are using a different database then you should put a similar check into the object.
After all that validation, I do a try catch that will open the Entity Framework connection and call our LogEvent. If it fails it throws an error message. Depending on what the application is, I will either leave a generic error that tells me that the event writing failed or use the ex.Message from the exception I caught. I left the infrastructure for both in so if you want the other then swap the “Event writing has failed” message for ex.Message.
Almost all of these are database timeout errors from mucking up the connection string or dealing with a poorly nonperforming web service call.
Let call it.
Call Logging function
I am going to drop a button on the form and double click to get to its click event.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace LoggingExample { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { EventLog.writeEvent("This is a title of the event.", "The system message placeholder."); } } }
Spin it up and press the button. A whole bunch of nothing occurs but lets look in the database.
So there you go. If you need a super easy custom logging setup, you could do worse than this one. 15 minutes start to finish and you still have time to get a cup of coffee.
Let me know how you handle situations where you need custom logging down in the comments below.