Friday, 2 June 2017

Using Log4Net in Windows Applications

Log4net is an open source library that allows .NET applications to log output to a variety of sources. The log4net library can be downloaded from the project homepage.
Log4net provides a simple mechanism for logging information to a variety of sources. Information is logged via one or more loggers. These loggers provide 5 levels of logging:
1. Debug
2. Information
3. Warnings
4. Error
5. Fatal
The extent of logging done by each level mentioned in above list decreases on going down the list.

Using Log4Net in application

1. Get the DLL :
The log4net library can be downloaded from the project homepage and can be added as a reference in the project used for logging in the .net solution.
2. Configuration [Configuration can be done directly in app.config or separate .config file can also be created]
    a. Configure directly in app.config
   1: <configSections>
   2:     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
   3:   </configSections>
   4:  
   5: <log4net debug="true">
   6:     <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
   7:       <file type="log4net.Util.PatternString" value="${TMP}\MyProject\Logs\Log_%env{USERNAME}_%date{yyyyMMdd}.log" />
   8:       <appendToFile value="true" />
   9:       <bufferSize value="20" />
  10:       <LockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
  11:       <layout type="log4net.Layout.PatternLayout">
  12:         <header type="log4net.Util.PatternString" value="[Log Starts]%newline" />
  13:         <footer type="log4net.Util.PatternString" value="[Log Ends]%newline%newline" />        
  14:         <conversionPattern value="%date [%username] - %message%newline" />
  15:       </layout>
  16:     </appender>
  17:  
  18:     <!-- Specify the level for some specific categories -->
  19:     <logger name="MyApplicationDebugLog">
  20:       <level value="DEBUG" />
  21:       <appender-ref ref="LogFileAppender" />
  22:     </logger>
  23:   </log4net>
    b. Using separate .config file [myLog4net.config]
   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <log4net debug="true">
   3:   <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
   4:     <file type="log4net.Util.PatternString" value="${TMP}\SRG\Logs\Log_%env{USERNAME}_%date{yyyyMMdd}.log" />
   5:     <appendToFile value="true" />
   6:     <bufferSize value="20" />
   7:     <LockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
   8:     <layout type="log4net.Layout.PatternLayout">
   9:       <header type="log4net.Util.PatternString" value="%newline[Log Starts]" />
  10:       <footer type="log4net.Util.PatternString" value="%newline[Log Ends]" />
  11:       <conversionPattern value="%newline%date [%username] - %message" />
  12:     </layout>
  13:   </appender>
  14:  
  15:   <!-- Specify the level for some specific categories -->
  16:   <logger name="MySRGApplicationDebugLog">    
  17:     <level value="DEBUG" />
  18:     <appender-ref ref="LogFileAppender" />
  19:   </logger>
  20: </log4net>
3. Configure the Logger in code
   1: public class LogHelper
   2: {
   3:     private static readonly ILog _debugLogger;
   4:     private static ILog GetLogger(string logName)
   5:     {
   6:         ILog log = LogManager.GetLogger(logName);
   7:         return log;
   8:     }
   9:  
  10:     static LogHelper()
  11:     {
  12:         //logger names are mentioned in <log4net> section of config file
  13:         _debugLogger = GetLogger("MyApplicationDebugLog");
  14:     }
  15:  
  16:     /// <summary>
  17:     /// This method will write log in Log_USERNAME_date{yyyyMMdd}.log file
  18:     /// </summary>
  19:     /// <param name="message"></param>
  20:     public static void WriteDebugLog(string message)
  21:     {
  22:         _debugLogger.DebugFormat(message);
  23:     }
  24: }
4. AssemblyInfo.cs file changes [This file can be find in Properties of the project where LogHelper class is created in Step 3]
a. //if app.config is used, add below line in file
[assembly: XmlConfigurator(Watch = true)]
Or
b. //if separate myLog4net.config is used, add below line in file
[assembly: XmlConfigurator (ConfigFile = "myLog4net.config", Watch = true)]
5. Use it in code
LogHelper.WriteDebugLog(message);
6. Output
Logs will be created at C:\Documents and Settings\<userid>\Local Settings\Temp\<ProjectName>\Logs as mentioned in file tag in configuration file with name as Log_<userid>_date.log
Log file will look like below:-
   1: [Log Starts]
   2: 2012-11-20 18:10:16,668 [domain\userid] - Application started
   3: 2012-11-20 18:12:23,121 [domain\userid] – Calling Load Module Method
   4: 2012-11-20 18:13:09,433 [domain\userid] - Application stopped
   5: [Log Ends]
Issues which might generally come
1. Header and footer coming more times than expected.
Putting XmlConfigurator.Configure() at more than one places may result in having more than one header and footer pair. XmlConfigurator.Configure() should be defined only at one place in the solution. Better approach is to put it in AssemblyInfo.cs as done in example above.
You don’t need below line in LogHelper Class
XmlConfigurator.Configure();
If you have below line in AssemblyInfo.cs class.
[assembly: XmlConfigurator(Watch = true)]
2. Log4net not logging when running application using .exe file instead of visual studio
To avoid this problem move the XMConfigurator configure code from LogHelper class to AssemblyInfo.cs file as done in above example
Summary:
Log4net is a simple and efficient mechanism to do logging in application. By considering above Issues, one can handle unwanted errors in logging which might get noticed during testing.

Grouping Query results by multiple columns in Linq

Recently I came across a requirement where I had to create a custom excel report based on grouping concept of SQL. Usage of Group by clause and Group new clause are already there on internet but there combined usage and creation of custom columns in excel is something I felt is missing. So here is my blog solving such problems:-
Assuming 2 tables as shown below:-
1. Machine Table
image
2. Event Table
image
Combined Result in SQL
image
Required Output in excel format
image
In this example, we are grouping by UniqueId and also formatting the columns Action and GroupName.
We will create a entity class for Report as shown below, List all columns that are needed in excel Report. I have added UniqueId, Action and Group.
   1: public class Report
   2: {        
   3:     public string UniqueId { get; set; }       
   4:     public string ActionName { get; set; }
   5:     public string GroupName { get; set; }
   6: }
Below Method ‘GenerateCustomReport’ returns List of Report object, this return value can be exported into excel in calling function.
In this method, we are performing following steps:-
1. Read table data into variable (I am using ADO.Net Entity framework in this case, you can also use SQL methods to read data from Machine and Event tables)
2. Using Linq to join tables and group by Unique Id and store result in ‘Report’ Entity format.
3. Iterate through ‘Report’ Entity result. Customize Action and Group Column as per the requirement. Here I am appending values with counter number like 1., 2. … etc.
   1: public List<Report> GenerateCustomReport()
   2: {
   3:     List<Report> finalReportList = new List<Report>();
   4:  
   5:     var ctx = new DMT2ModelContainer();
   6:     var EventLogs = ctx.EventLogs.ToArray();
   7:     var Machines = ctx.Machines.ToArray();
   8:  
   9:     var result = from eventLog in EventLogs
  10:                  join machine in Machines on eventLog.MachineId equals machine.Id
  11:                  group new { machine.UniqueId, eventLog.CMActionName, eventLog.CMGroupName } by machine.UniqueId into report
  12:                  select report;
  13:  
  14:     foreach (var groupedRow in result)
  15:     {
  16:         Report finalReportRow = new Report();
  17:         int counter = 1;
  18:         foreach (var row in groupedRow)
  19:         {
  20:             finalReportRow.UniqueId = row.UniqueId;
  21:             finalReportRow.ActionName += counter + ". " + row.CMActionName + Environment.NewLine;
  22:             finalReportRow.GroupName += counter + ". " + row.CMGroupName + Environment.NewLine;
  23:             counter++;
  24:         }
  25:         finalReportList.Add(finalReportRow);
  26:     }
  27:  
  28:     return finalReportList;
  29: }
I have focused on column customization and group by logic. On how to create excel and exporting data into excel , there are multiple blogs available on internet that shows how to achieve this functionality.
You may need to call the above method and export the return data into excel. The final output will look like below:-
image
Write to me for suggestions or feedback.
Till then, Happy Coding!!!

SharePoint O365 List – Remove Hyperlink from Title Column

Hi All,
I had a requirement in my project where I had to remove the hyperlink from the Title column and add another hyperlink column with different display name like ‘View Entry’. We will see in this blog on how to achieve this
1. Remove hyperlink from Title column.
2. Add Hyperlink in ‘Multiple lines of Text’ column with different display name in list item while adding.
In this example, I have a SharePoint List with multiple columns like View, Title, Status etc. as shown in image below.
clip_image002
If you click on the any list item Title Column hyperlink, you will be navigated to DispForm page for more details of the list item as shown in image below.
clip_image004
To remove this hyperlink follow below steps:-
1. Go to Ribbon -> List -> Modify View
clip_image006
2. Scroll down the page to the place where Columns and their positions are listed.
3. Uncheck Title (linked to Item with Edit Menu) column and make a note of Position value.
clip_image008
4. Scroll down the list and notice that there are 2 more columns for Title. As we have to select column with no hyperlink, Select Title column
clip_image010
5. Choose position as noticed in step 3 and Save the Changes.
clip_image012
6. Now if you go back to you list, you can notice the hyperlink has been removed. Hurray!!!
clip_image014
Moving to our next objective – having hyperlink in ‘multiple lines of text’ column with different display name in list item.
1. Go to Ribbon -> List -> List Settings
clip_image016
2. Scroll down the page to read column lists. I already have a multiple lines of text column added
clip_image018
clip_image020
3. You may need to add this column by clicking on ‘Create Column’. Please note Type of text is – Enhanced rich text (Rich Text with pictures, tables, and hyperlinks)
clip_image022
After this is done, below is the code in C# to add hyperlink to the ‘View’ Column.
string myUrl = "https://microsoft.sharepoint.com/teams/XXX/Lists/MyListName/DispForm.aspx?ID=X";
oListItem["View"] = "<a href='" + myUrl + "'> View Entry... </a>";
The full code to add a list item in SharePoint List will look like below:-
ClientContext clientContext = new ClientContext("SPO365Url");
 
SecureString securePassword = new SecureString();
foreach (char passwordChar in "SPPassword".ToCharArray())
{
    securePassword.AppendChar(passwordChar);
}
 
clientContext.Credentials = new SharePointOnlineCredentials("SPUserName", securePassword);
Web web = clientContext.Web;
clientContext.Load(web);
List list = clientContext.Web.Lists.GetByTitle("SPListName");
 
ListItemCreationInformation listCreationInformation = new ListItemCreationInformation();
Microsoft.SharePoint.Client.ListItem oListItem = list.AddItem(listCreationInformation);
string myUrl = "https://microsoft.sharepoint.com/teams/XXX/Lists/MyListName/DispForm.aspx?ID=X";
oListItem["View"] = "<a href='" + myUrl + "'> View Entry... </a>";
oListItem["Title"] = "SP_Issue_Title";
oListItem["Status"] = "SP_Issue_Status";
oListItem["Issue Description"] = "SP_Issue_Description";
oListItem["Action Items"] = "SP_Action_Items";
oListItem["Issue ID"] = "SP_Issue_ID";
clientContext.ExecuteQuery();
All feedback and suggestions are welcome.
Happy Coding!!!