Tuesday, December 11, 2012

ASP.Net - Logging

Hello


Log4net



The Basics


Three parts : configuration , setup , call

Configuration - done via app.config (desktop application) or web.config (web application)

Setup - set up and instantiate connection with Log4Net

Call - write to log



Logging Levels


There are 7 logs level  arranged from the highest priority at the top



  1. OFF - nothing gets logged (cannot be called)
  2. FATAL
  3. ERROR
  4. WARN
  5. INFO
  6. DEBUG
  7. ALL - everything gets logged (cannot be called)




The Configuration


Root
You must have one root section.
It house the minimal level for all logger. 
All logger inherit it




<root>
  <level value="INFO"/>
  <appender-ref ref="FileAppender"/>
  <appender-ref ref="TraceAppender" />
</root>

Additional Loggers
Sometimes you will want to know more about a particular part of your application. log4net anticipated this by allowing you to specify additional logger references beyond just the root logger. For example, here is an additional logger that I have placed in our config file to log to the output messages that occur inside the OtherClass class object:
<logger name="Log4NetTest.OtherClass">
  <level value="DEBUG"/>
  <appender-ref ref="TraceAppender"/>
</logger>
Note that the logger name is the full name of the class including the namespace. If you wanted to monitor an entire namespace, it would be as simple as listing just the namespace you wanted to monitor. I would recommend against trying to re-use appenders in multiple loggers. It can be done, but you can get some unpredictable results.



In a config file where there will (potentially) be more information stored beyond just the log4net configuration information, you will need to specify a section to identify where the log4net configuration is housed. Here is a sample section that specifies that the configuration information will be stored under the XML tag "log4net":
<configSections>
  <section name="log4net" 
    type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>




Appender (General)

An appender is the name for what logs the information. It specifies where the information will be logged, how it will be logged, and under what circumstances the information will be logged. While each appender has different parameters based upon where the data will be going, there are some common elements. The first is the name and type of the appender. Each appender must be named (anything you want) and have a type assigned to it (specific to the type of appender desired). Here is an example of an appender entry:
<appender name="TraceAppender" type="log4net.Appender.Trace
Appender">





Layout

Inside of each appender must be a layout section. This may be a bit different depending on the type of appender being used, but the basics are the same. You need a type that specifies how the data will be written. There are multiple options, but the one that I suggest you use is the pattern layout type. This will allow you to specify how you want your data written to the data repository. If you specify the pattern layout type, you will need a sub-tag that specifies a conversion pattern. This is the pattern by which your data should be written to the data repository. I will give a more detailed description of your options for the conversion patterns, but for now, here is an example of the layout tag with the pattern layout specified:
<layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%ndc] 
    - %message%newline"/>
    </layout>





Conversion Patterns


Filters


See http://www.codeproject.com/Articles/140911/log4net-Tutorial




Appenders

Each type of appender has its own set of syntax based upon where the data is going. The most unusual ones are the ones that log to databases. I will list a few of the ones that I think are most common. However, given the above information, you should be able to use the examples given online without any problems. The log4net site has some great examples of the different appenders. As I have said before, I used the log4net documentation extensively and this area was no exception. I usually copy their example and then modify it for my own purposes.





Trace Appender

It writes to the output window. This particular filter outputs a value like "2010-12-26 15:41:03,581 [10] WARN Log4NetTest.frmMain - This is a WARN test." It will include a new line at the end.

<appender name="TraceAppender" type="log4net.Appender.TraceAppender">  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date{ABSOLUTE} 
    [%thread] %level %logger - %message%newline"/>
  </layout>
  <filter type="log4net.Filter.StringMatchFilter">
    <stringToMatch value="test" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />
</appender>






File Appender

This appender will write to a text file. The big differences to note here are that we have to specify the name of the text file (in this case, it is a file named mylogfile.txt that will be stored in the same location as the executable), we have specified that we should append to the file (instead of overwriting it), and we have specified that the FileAppender should use the Minimal Lock which will make the file usable by multiple appenders.
<appender name="FileAppender" type="log4net.Appender.FileAppender">
  <file value="mylogfile.txt" />
  <appendToFile value="true" />
  <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
  </layout>
  <filter type="log4net.Filter.LevelRangeFilter">
    <levelMin value="INFO" />
    <levelMax value="FATAL" />
  </filter>
</appender>




Rolling File Appender

This is an appender that should be used in place of the file appender whenever possible. The purpose of the rolling file appender is to perform the same functions as the file appender but with the additional option to only store a certain amount of data before starting a new log file. This way, you won't need to worry about the logs on a system filling up over time. Even a small application could overwhelm a file system given enough time writing to a text file if the rolling option were not used. In this example, I am logging in a similar fashion to the file appender above, but I am specifying that the log file should be capped at 10MB and that I should keep up to 5 archive files before I start deleting them (oldest gets deleted first). The archives will be named with the same name as the file, only with a dot and the number after it (example:mylogfile.txt.2 would be the second log file archive). The staticLogFileName entry ensures that the current log file will always be named what I specified in the file tag (in my case, mylogfile.txt).
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="mylogfile.txt" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <maxSizeRollBackups value="5" />
  <maximumFileSize value="10MB" />
  <staticLogFileName value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %level %logger - %message%newline" />
  </layout>
</appender>




ADO.NET Appender

See http://www.codeproject.com/Articles/140911/log4net-Tutorial



The Code

Once you have a reference to the log4net DLL in your application, there are three lines of code that you need to know about. The first is a one-time entry that needs to be placed outside of your class. I usually put it right below my using statements in the Program.cs file. You can copy and paste this code since it will probably never need to change (unless you do something unusual with your config file). Here is the code:
[assembly: log4net.Config.XmlConfigurator(Watch = true)]
The next entry is done once per class. It creates a variable (in this case called "log") that will be used to call the log4net methods. This code is also code that you can copy and paste (unless you are using the Compact Framework). It does a System.Reflection call to get the current class information. This is useful because it allows us to use this code all over but have the specific information passed into it in each class. Here is the code:
private static readonly log4net.ILog log = log4net.LogManager.GetLogger
    (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
The final code piece is the actual call to log some piece of information. This can be done using the following code:
log.Info("Info logging");
Notice that you can add an optional parameter at the end to include the exception that should be logged. Include the entire exception object if you want to use this option. The call is very similar, and it looks like this:
log.Error("This is my error", ex);
ex is the exception object. Remember that you need to use the %exception pattern variable in your appender to actually capture this exception information.


Config File Template

While you can look at the example code I have posted to see a config file in action, based upon some of the difficulties people were experiencing, I decided to post a config file template to help readers visualize where each of the config file pieces will go. I have given you a blank template below. I have also labeled each section with which level it is in so that, in case the formatting doesn't make it obvious, you know how each item relates to all the others up and down the tree.
<!--This is the root of your config file-->
<configuration> <!-- Level 0 -->
  <!--This specifies what the section name is-->
  <configSections> <!-- Level 1 -->
    <section name="log4net" 
      type="log4net.Config.Log4NetConfigurationSectionHandler, 
            log4net"/> <!-- Level 2 -->
  </configSections>
  <log4net> <!-- Level 1 -->
    <appender>  <!-- Level 2 -->
      <layout>  <!-- Level 3 -->
        <conversionPattern />  <!-- Level 4 -->
      </layout>
      <filter>  <!-- Level 3 -->
      </filter>
    </appender>
    <root> <!-- Level 2 -->
      <level /> <!-- Level 3 -->
      <appender-ref /> <!-- Level 3 -->
    </root>
    <logger> <!-- Level 2 -->
      <level /> <!-- Level 3 -->
      <appender-ref /> <!-- Level 3 -->
    </logger>
  </log4net>
</configuration>

FAQs

  1. Why can't I write to my text file? log4net runs under the privileges of the active user. Make sure that the active user has rights to create/modify/delete the specified text file.
  2. Why aren't certain events being logged? Most likely, this is because you have a filter in place. Remember that the root entry can have an overall minimum log filter that specifies no event gets logged below this level. The actual appender can also have a level filter, or you could have the deny all in place too early.
  3. Why am I logging events that I don't want? Most likely you have set a filter improperly. Either you forgot the deny all filter, or you included a level range filter first.
  4. Why am I getting a compile error? If you get an error similar to "The referenced assembly "log4net" could not be resolved because it has a dependency on "System.Web, Version=4.0.0.0 ...", then you haven't changed your target framework to ".NET Framework 4". Change that and this error, along with others that cascade from it, will go away.  Note: this has been fixed in the new version of log4net.  You should no longer need to do this.
  5. How do I use this in ASP.NET? Using this information in ASP.NET is the same as using it in a desktop application. The major difference is that the config file is the web.config file instead of the app.config file.
  6. Why can't I get my ADO.NET appender to log anything? If you look over all of the settings and they look right, chances are that you are experiencing the pain of the bufferSize configuration. Change thebufferSize to 1 and it will attempt to log every message you send right away. If this still does not work, the issue is your configuration.



Source sample
Following is a simple ASP.Net sample
WebSiteTestLog4Net

Default.aspx.cs
[assembly: log4net.Config.XmlConfigurator(Watch = true)]




public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        log.Debug("Debug logging _Default");
        log.Info("Info logging _Default");
        log.Warn("Warn logging _Default");
        log.Error("Error logging _Default");
        log.Fatal("Fatal logging _Default");
        Class1.callMe();
    }




    private static readonly log4net.ILog log = log4net.LogManager.GetLogger
    (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}


Class1.cs
/// 
/// Summary description for Class1
/// 
public class Class1
{
 public static void callMe()
    {
        log.Debug("Debug logging Class1");
        log.Info("Info logging Class1");
        log.Warn("Warn logging Class1");
        log.Error("Error logging Class1");
        log.Fatal("Fatal logging Class1");
    }
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger
    (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}


Web.config



  
    
Running the application will produce the following :

A file name mylogfile1 under the web site root :



Visual studio Output windows



Sources :






Nathan

Monday, August 13, 2012

ASP.Net - Validation Controls

Hello

Post contents :

  • Introduction
  • Validation Controls
  • RequiredFieldValidator - verify the user will insert any input
  • CompareValidator - compare vs a value
  • RangeValidator -  compare vs a range of values
  • RegularExpressionValidator - check vs regular expression
  • ValidationSummary - display all validation error in the page
  • CustomeValidator - check vs your own validation method (full flexibility)

Introduction


Validation process checks that the user input (Text,Checked,..) is correct otherwise error is issued. E.g. in case a number should be entered  : validation check that the user will not be able to insert a letter, or to be more accurate that this letter will not be sent to the server and an error will be issued.

Validation is performed in general on the client (preferred) in case it support JavaScript otherwise it is done on the server.


ASP.Net provides few controls to perform input validation.


Validation Controls

There are six validation controls in ASP.Net
  • RequiredFieldValidator - verify the user will insert any input
  • CompareValidator - compare vs a value
  • RangeValidator -  compare vs a range of values
  • RegularExpressionValidator - check vs regular expression
  • ValidationSummary - display all validation error in the page
  • CustomeValidator - check vs your own validation method (full flexibility)


It is important to note that :
  • Validation take place when user submit info to the server e.g. when a button is clicked or in any other way which cause POSTBACK  (not on Page_Load).
  • Page has a property IsValid which indicate if validation is ok for this page. It is based on the validation controls in this page. IsValid is false if at least one validator has IsValid=false otherwise it is true
  • It is possible to use  Response.Redirect("some url") in conjunction with Page.IsValid to indicate success validation



RequiredFieldValidator 
Validate that a field is not empty

Important properties :
  • ControlToValidate - id of control that should be validated
  • ErrorMessage - text to display in  ValidationSummary in case validation failed. This will appears for this control in case of error if Text property is empty
  • Text - text to display in case validation failed
  • EnableClientScript - whether to perform validation in client side if possible (default true)
  • IsValid - whether validation is ok or not
  • Display :
  • None - no error display, only in ValidationSummary 
  • Static - space required for error is occupied anyway
  • Dynamic - space required for error is occupied only when error occurs  

Sample 1
RequiredFieldValidator - sample 1.zip

The sample is a web site with info the user has to fill :

  • Name - required
  • ID - required
  • E-Mail - optional
  • Cell Phone - optional
Error should be issued in case Name or ID are not entered
Redirect to "thank you" page in case validation is not ok. 


View in browser and click on Submit :


Use breakpoint inside the Submit handler. When does the breakpoint is hit ? in case validation is ok or not ok ? why ? where is validation done in client or server ?

In case validation is OK use thank you page :

  • use Page.IsValid  (Validation will not be OK if at least one validator has failed.)
  • use Response.Redirect("Url name")



CompareValidator 
Validate the input against another value for a given operator

Important properties :
  • ControlToCompare - compare this control with the value entered by the user. E.g. in case the input is entered viea TextBox you can compare the Text property with another TextBox which is the ControlToCompare.
  • ValueToCompare - compare this value with the value entered by the user
  • Operator :
  • Equal
  • GreaterThan
  • GreaterThanEqual
  • LessThan
  • LessThanEqual
  • NotEqual
  • DataTypeCheck - validate type is correct (Type should also be defined)
  • Same as in RequiredFieldValidator  
  • ControlToValidate 
  • ErrorMessage 
  • Text  
  •  EnableClientScript 
  •  Display 

Remark
  • validation will be ok in case the value to compare is empty , use also RequiredFieldValidator in this case (see )


Sample 2
The sample is a web site with info the user has to fill  - guess day of birth.

Handle empty field (use RequiredFieldValidator, Display = dynamic)

Handle wrong value (use CompareValidator,Display = dynamic)



Handle correct value  (use Page.IsValid and Response.Redirect)



Use breakpoint inside the "Guess my day of birth" handler. When does the breakpoint is hit ? in case validation is ok or not ok ? why ? where is validation done in client or server ?




RangeValidator 
Validate the input against a range


Important properties :
  • MinimumValue - allowed input minimal value
  • MaximumValue - allowed input maximal value
  • Type Specifies the data type of the value to check. The types are:
    • Currency
    • Date
    • Double
    • Integer
    • String
  • Same as in RequiredFieldValidator  
  • ControlToValidate 
  • ErrorMessage 
  • Text  
  •  EnableClientScript 
  •  Display 
Remark
  • validation will be ok in case the value to compare is empty , use also RequiredFieldValidator in this case (see )
Sample 3
RangeValidator_sample 3.zip
The sample is a web site with info the user has to fill  - height.
Common height is defined between 120-220



Issue a note in case the value is in not in range




 Redirect the page in case height is ok  (120-220)


RegularExpressionValidator 
Validate that the input matches a regular expression

Important properties :
  • ValidationExpression
  • Same as in RequiredFieldValidator  
  • ControlToValidate 
  • ErrorMessage 
  • Text  
  •  EnableClientScript 
  •  Display 


Remark
  • validation will be ok in case the value to compare is empty , use also RequiredFieldValidator in this case (see )


You can choose via ValidationExpression regular expression from a list :




Sample 4
RegularExpressionValidator sample 4.zip
The sample is a web site with Email that need to be validated.

Non valid case


In case the mail is valid the page is redirected to result in :

Need to handle also empty field.

ValidationSummary
Display error messages from different validation controls (provided that their ErrorMessage property is not empty)


Important properties :
  • ShowSummary - define whether the summary control should be visible or not
  • ShowMessageBox - defines whether to show errors in message box
  • DisplayMode :
  • BulletList
  • List
  • SingleParagraph


Sample 5
 ValidationSummary sample 5.zip


 Click on button 



CustomeValidator
Validate the user input against a custom method.

Important event

  • ServerValidate - occurs when server validate e.g. when button is pressed 


Important properties of ServerValidateEventArgs :

  • Value - value to be validate
  • IsValid - validation result


Important properties of CustomeValidator:
  • Same as in RequiredFieldValidator  
  • ControlToValidate 
  • ErrorMessage 
  • Text  
  •  EnableClientScript 
  •  Display 

Remark
  • Validation will be ok in case the value to compare is empty , use also need RequiredFieldValidator in this case ( see)
  • The handler of  ServerValidate will NOT be invoked when another validation control like  RequiredFieldValidator is used and it's IsValid is false. This is because the validation of  RequiredFieldValidator is done on the client and when failed no info is sent to the server.


Double click on the control result in :



Sample 6
CustomeValidator_sample 6.zip
Build the following web site :

user name :

  • length should be between 1-20 
  • only lower case letters are allowed
  • issue an error in case rules are violated

password :

  • length should be between 5-10
  • only upper case letters are allowed
  • issue an error in case rules are violated
Redirect to Login Success page in case validation is OK


Example for the case ServerValidate is not called because client validation has failed - User is empty is issued by RequiredFieldValidator which validate on the client




CauseValidation
CauseValidation_sample 7

It is possible to cause validation even if a button is not pressed.

This can be done by setting the following input control properties :

  • CauseValidation = true
  • AutoPostBack  = true

Then by putting the mouse in the input (e.g. TextBox,RadioButton ...) and typing some thing (even space) then use Enter ot Tab will cause AutoPostBack and validation will be performed


Run to result :



Now put the mouse inside one of the TextBox and hit space, then click Enter or Tab, this cause POSTBAACK which cause validation which result in :



Home Project
Add the following validation to the home project of http://websoftwarenk.blogspot.co.il/2012/08/aspnet-web-form-using-server-control.html :

  • Validate "Choose Starting Player"
  • Validate "Choose Background" 
  • Text should be * and error should be displayed by summary validator
  • Validate  x Palyer \ 0 Player TextBox name :
  • name is not empty
  • name length is 5-20
  • name contains only letters and digits
  • Text should be * and error should be displayed by summary validator


Nathan