Alternatives to .NET Reflector

After Red Gate’s recent announcement (http://www.red-gate.com/products/dotnet-development/reflector/announcement) that the .NET Reflector tool will become a paid app, I began to wonder (like many developers, I’m sure) about free and/or open source alternatives.

(No, I’m not wading into the political arguments about the change other than to say that the fact that the last free version of .Reflector is time-bombed… meaning that you’ll have to pay up or quit using the tool altogether… is disappointing.)

.NET Reflector

As you probably know, .NET Reflector is a tool for browsing and decompiling the contents of compiled .NET assemblies.  Originally created by Lutz Roeder and subsequently purchased by Red Gate software, it has always (until now) been a free utility.  Here is a screenshot of the tool in action:

Reflector

The Alternatives

A quick search turned up three potential replacements for .NET Reflector: ILSpy, Dotnet IL Editor, and MonoDevelop.  The following table gives an overview of what each of these tools has to offer.

TOOL .NET Reflector Dotnet IL Editor MonoDevelop ILSpy
PRICE $35 Free Free Free
OPEN SOURCE No Yes Yes Yes
SUPPORTS PLUG-INS Yes No Yes * Yes
DISASSEMBLER Yes Yes Yes Yes
DECOMPILER Yes No Yes Yes
.NET VERSION REQUIRED 4.0 2.0 3.5 4.0
VISUAL STUDIO INTEGRATION Yes No No No
CLICK NAVIGATION OF SOURCE Yes No Yes Yes

* MonoDevelop supports plug-ins.  However, the Assembly Browser itself is a plug-in, so it’s not immediately clear if a plug-in could be added to extend the functionality of the Assembly Browser.

Dotnet IL Editor

http://sourceforge.net/projects/dile/
This tool allows disassembling and debugging of .NET applications directly from the application assemblies.
Version 0.2.6 (latest stable build) is more than three years old, released on November 4, 2007.  Current weekly builds of version 0.2.7 (development appears to have restarted in mid-2010) are also available (those require .NET 4.0).  As I was only interested in the disassembling features of the tool, I did not see a difference between the two versions.  Both 32-bit and 64-bit versions are available, which is nice.

There is no formal installation package for this tool; just download the ZIP archive, unpack it, and run the dile.exe executable.

dile

The major downside of this tool that immediately presented itself is that it is ONLY a disassembler, meaning that it produces only IL rather than C# or VB.NET.  Because of this, it is not a tool that I expect too many developers will view and a reasonable replacement for .NET Reflector.

MonoDevelop Assembly Browser

http://monodevelop.com/
You may be familiar with MonoDevelop; it is an open source IDE designed to bring .NET development to any platform (i.e.  Linux and OSX in addition to Windows).  What it not as well known is the Assembly Browser that is included with MonoDevelop.  This tool was highlighted by Miguel de Icaza in his recent blog post: http://tirania.org/blog/archive/2011/Feb-04.html

In addition to .NET 3.5, MonoDevelop requires GTK# 2.12.9 for installation.  GTK# is a separate download and installation process, which is a little bit of a barrier to use.  The other tools I’m highlighting, including Reflector itself, are very easy to set up and use.  MonoDevelop requires a bit more effort.

Once I had MonoDevelop installed, it’s not immediately obvious where to find the Assembly Browser.  Here’s how to do it.  In MonoDevelop, choose to open an assembly like any other file, but specify the Assembly Browser to open it.  (There’s an extra drop-down on the file/open menu that allows you to specify Assembly Browser… the other option there is “Solution Workbench”).  When you choose Assembly Browser, you get a familiar Reflector-like interface to the contents of the assembly.

MonoDevelop

The Assembly Browser can produce both IL and C# output from the decompiler.  The displayed source can be copied and saved, though I was only able to do this one method or property at a time.

ILSpy from SharpDevelop

http://wiki.sharpdevelop.net/ILSpy.ashx
At this writing, the tool is in it’s infancy.  Introduced in February 2011 (immediately following Red Gate’s announcement of the fate of .NET Reflector), is is the marriage of a decompiler that was a developer’s university dissertation that had been lying on his hard drive a couple years (http://community.sharpdevelop.net/blogs/dsrbecky/archive/2011/02/11/ilspy-decompiler.aspx) and a new UI (http://community.sharpdevelop.net/blogs/danielgrunwald/archive/tags/ILSpy/default.aspx). 

The developers themselves note that the tool still needs a lot of work (http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/02/04/ilspy-a-new-net-assembly-inspector.aspx).  On the day that I downloaded the binaries, there were 12 builds available for download from that day alone… so it is obviously very early in the development cycle.  However, the SharpDevelop folks have produced solid tools in the past, so I am optimistic about this one.

ILSpy has no formal installation package; just download, unzip, and run.  My first impression upon running the tool was that it looks just like reflector.

ILSpy

ILSpy produces IL and C# output, and reporting VB.NET is also in the works.  There is an option native to the app to save the decompiled source code (so no need to copy and paste).  The source for entire classes can be saved at once.

Test Results

So, how do these tools perform?  In other words, how well to the decompiler features work?  As the decompiler is the killer feature of .NET Reflector, that’s what these tools need to get right in order to be viable replacements.

Below is the source code of an assembly I used to test these tools.  This was compiled three separate times: once as a .NET 2.0 assembly, once as a .NET 3.5 assembly, and once as a .NET 4.0 assembly.  For the 3.5 assembly, a class that uses LINQ was added to the code.  For the 4.0 assembly, a method that uses a dynamic was added.  This mix of functionality and framework targets provided multiple testing scenarios.

The following code is the version that was compiled with a.NET 4.0 target; the “extra” class and method that exist here (but not in the 2.0 and 3.5 versions) are noted in code comments. 

using System;
using System.Collections.Generic;
using System.Linq;      // Add this when compiling to .NET 3.5
using Microsoft.CSharp; // Add this when compiling to .NET 4.0

namespace Geometry
{
   public interface IShape
   {
       double GetArea();
   }

   public abstract class Shape : IShape
   {
       public virtual double GetArea()
       {
           throw new NotImplementedException();
       }
   }

   public class Circle : Shape
   {
       private const double pi = 3.14159265;

       private double _radius = 0;
       public double Radius
       {
           get { return _radius; }
           set { _radius = value; }
       }

       public override double GetArea()
       {
           return 2 * pi * _radius;
       }
   }

   public class Square : Shape
   {
       private double _height = 0;
       public double Height
       {
           get { return _height; }
           set { _height = value; }
       }

       private double _width = 0;
       public double Width
       {
           get { return _width; }
           set { _width = value; }
       }

       public override double GetArea()
       {
           return _height * _width;
       }
   }

   public class Triangle : Shape
   {
       private double _base = 0;
       public double Base
       {
           get { return _base; }
           set { _base = value; }
       }

       private double _height = 0;
       public double Height
       {
           get { return _height; }
           set { _height = value; }
       }

       public override double GetArea()
       {
           return 0.5 * _base * _height;
       }
   }

   // Add this class when compiling to .NET 3.5
   public class ShapeGroup
   {
       private List<IShape> _shapes = new List<IShape>();
       public List<IShape> Shapes
       {
           get { return _shapes; }
       }

       public void Add(IShape shape)
       {
           _shapes.Add(shape);
       }

       public void Clear()
       {
           _shapes.Clear();
       }

       public double GetTotalArea()
       {
           var totalArea = from n in _shapes select n.GetArea();
           return totalArea.Sum();
       }

       // Add this method when compiling to .NET 4.0
       public double GetShapeArea(int index)
       {
           dynamic shape = _shapes[index];
           return shape.GetArea();
       }
   }
}

 

.NET Reflector

In order to compare the alternatives to the original, included here is the decompiler output of .NET Reflector itself for the Square and ShapeGroup classes.  You can see that the GetShapeArea method looks a bit different from the original source code, but otherwise the output is very close to the original.

public class Square : Shape
{
   // Fields
   private double _height;
   private double _width;

   // Methods
   public override double GetArea()
   {
       return (this._height * this._width);
   }

   // Properties
   public double Height
   {
       get
       {
           return this._height;
       }
       set
       {
           this._height = value;
       }
   }

   public double Width
   {
       get
       {
           return this._width;
       }
       set
       {
           this._width = value;
       }
   }
}

public class ShapeGroup
{
   // Fields
   private List<IShape> _shapes = new List<IShape>();

   // Methods
   public void Add(IShape shape)
   {
       this._shapes.Add(shape);
   }

   public void Clear()
   {
       this._shapes.Clear();
   }

   public double GetShapeArea(int index)
   {
       object shape = this._shapes[index];
       if (<GetShapeArea>o__SiteContainer2.<>p__Site3 == null)
       {
           <GetShapeArea>o__SiteContainer2.<>p__Site3 = CallSite<Func<CallSite, object, double>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(double), typeof(ShapeGroup)));
       }
       if (<GetShapeArea>o__SiteContainer2.<>p__Site4 == null)
       {
           <GetShapeArea>o__SiteContainer2.<>p__Site4 = CallSite<Func<CallSite, object, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "GetArea", null, typeof(ShapeGroup), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
       }
       return <GetShapeArea>o__SiteContainer2.<>p__Site3.Target(<GetShapeArea>o__SiteContainer2.<>p__Site3, <GetShapeArea>o__SiteContainer2.<>p__Site4.Target(<GetShapeArea>o__SiteContainer2.<>p__Site4, shape));
   }

   public double GetTotalArea()
   {
       return this._shapes.Select<IShape, double>(delegate (IShape n) {
           return n.GetArea();
       }).Sum();
   }

   // Properties
   public List<IShape> Shapes
   {
       get
       {
           return this._shapes;
       }
   }
}

 

Dotnet IL Editor

I couldn’t easily capture the output for an entire class or assembly at once using the DotNet IL Editor, so here is the output for the GetTotalArea method of the ShapeGroup class. This is one of the more interesting methods in the class, as it uses LINQ.  As mentioned earlier, this tool is a disassembler only, so IL is the only output possible.

.method public hidebysig instance float64 GetTotalArea() cil managed
{
   // Code size 48 (0×30)
   .maxstack 4
   .locals init (class [mscorlib]System.Collections.Generic.IEnumerable`1<float64> V_0)
   ldarg.0
   ldfld class [mscorlib]System.Collections.Generic.List`1<class Geometry.IShape> Geometry.ShapeGroup::_shapes
   ldsfld class [mscorlib]System.Func`2<class Geometry.IShape, float64> Geometry.ShapeGroup::CS$<>9__CachedAnonymousMethodDelegate1
   brtrue.s IL_001e
   ldnull
   ldftn float64 Geometry.ShapeGroup::<GetTotalArea>b__0(class Geometry.IShape)
   newobj instance void class [mscorlib]System.Func`2<class Geometry.IShape, float64>::.ctor(object, native int)
   stsfld class [mscorlib]System.Func`2<class Geometry.IShape, float64> Geometry.ShapeGroup::CS$<>9__CachedAnonymousMethodDelegate1
   ldsfld class [mscorlib]System.Func`2<class Geometry.IShape, float64> Geometry.ShapeGroup::CS$<>9__CachedAnonymousMethodDelegate1
   call
   stloc.0
   ldloc.0
   call float64 [System.Core]System.Linq.Enumerable::Sum(class [mscorlib]System.Collections.Generic.IEnumerable`1<float64>)
   ret
} //end of method Geometry.ShapeGroup::GetTotalArea

 

MonoDevelop

Interestingly, MonoDevelop was only able to decompile the .NET 2.0 test assembly without error.  It had trouble with the GetTotalArea method of the ShapeGroup class in both the 3.5. and 4.0 versions of the assembly.  That is the method which makes use of LINQ.  I’m not sure if LINQ support is a known shortcoming of the current version of the Mono tools, but it highlights a significant drawback to using this assembly browser.

Here is the decompiled source for the Square and ShapeGroup classes, as produced by the Assembly Browser in MonoDevelop.

public class Square : Shape{
// Fields 
private double _height; 
private double _width; 

// Constructors public Square (); 

// Methods
public double GetArea() {
  return this._height * this._width;
}

// Properties
public double Height{
  get {
   public double get_Height()
   {
    return this._height;
   }
  }
  set {
   public void set_Height(double value)
   {
    this._height = value;
   }
  }
}

public double Height{
  get {
   public double get_Height()
   {
    return this._height;
   }
  }
  set {
   public void set_Height(double value)
   {
    this._height = value;
   }
  }
}
}

public class ShapeGroup{
// Fields
private List<IShape> _shapes;

[CompilerGenerated ()]
private static Func<IShape, double> CS$<>9__CachedAnonymousMethodDelegate1;

// Constructors
public ShapeGroup ();

// Methods
public void Add(IShape shape)
{
  this._shapes.Add(shape);
}

public void Clear()
{
  this._shapes.Clear();
}

public double GetTotalArea ();
{
Decompilation failed:
System.NotImplementedException: IL_000e: ldftn float64  Geometry.ShapeGroup::b__0(Geometry.IShape)
    at Cecil.Decompiler.Cil.BaseInstructionVisitor.OnLdftn(Instruction instruction)
    at Cecil.Decompiler.Cil.InstructionDispatcher.Dispatch(Instruction instruction, IInstructionVisitor visitor)
    at Cecil.Decompiler.Cil.BaseInstructionVisitor.Visit(Instruction instruction)
    at Cecil.Decompiler.StatementDecompiler.ProcessExpressionBlock(InstructionBlock block, Boolean skip_first)
    at Cecil.Decompiler.StatementDecompiler.ProcessExpressionBlock(InstructionBlock block)
    at Cecil.Decompiler.StatementDecompiler.PushConditionExpression(Instruction instruction)
    at Cecil.Decompiler.StatementDecompiler.TryProcessExpression(Instruction instruction)
    at Cecil.Decompiler.StatementDecompiler.ProcessInstruction(Instruction instruction)
    at Cecil.Decompiler.StatementDecompiler.ProcessInstructions(InstructionBlock block)
    at Cecil.Decompiler.StatementDecompiler.ProcessBlock(InstructionBlock block)
    at Cecil.Decompiler.StatementDecompiler.ProcessBlocks()
    at Cecil.Decompiler.StatementDecompiler.Run()
    at Cecil.Decompiler.StatementDecompiler.Process(DecompilationContext context, BlockStatement body)
    at Cecil.Decompiler.DecompilationPipeline.Run(MethodBody body)
    at Cecil.Decompiler.Extensions.RunPipeline(DecompilationPipeline pipeline, MethodBody body)
    at Cecil.Decompiler.Extensions.Decompile(MethodBody body, ILanguage language)
    at Cecil.Decompiler.Languages.CSharpWriter.Write(MethodDefinition method)
    at MonoDevelop.AssemblyBrowser.DomMethodNodeBuilder.Decompile(DomCecilMethod method, Boolean markup)
}

public double GetShapeArea(int index)
{
  CSharpArgumentInfo[] V_1;
  object V_0 = this._shapes.get_Item(index);
  if (!<GetShapeArea>o__SiteContainer2.<>p__Site3)
  {
   <GetShapeArea>o__SiteContainer2.<>p__Site3 = CallSite<Func<CallSite, object, double>>.Create(Binder.Convert(0, Type.GetTypeFromHandle(double), Type.GetTypeFromHandle(ShapeGroup)));
  }
}

[CompilerGenerated ()]
private abstract sealed class <a ref="T:Geometry.ShapeGroup.o__SiteContainer2"><GetShapeArea>o__SiteContainer2
{
  // Fields
  public static CallSite<Func<CallSite, object, double>> <>p__Site3;
  public static CallSite<Func<CallSite, object, object>> <>p__Site4;
}

private static double <GetTotalArea>b__0(IShape n)
{
  return n.GetArea();
}

// Properties
public List<IShape> Shapes{
  get {
   public List<IShape> get_Shapes()
   {
    return this._shapes;
   }
  }
}
}

 

ILSpy

The C# produced by ILSpy is good quality (a near reproduction of the original) for simple code.  However, when it decompiled the GetTotalArea() and GetShapeArea() methods on the ShapeGroup class, (which use LINQ and dynamics, respectively), the output was considerably different from the original (and from Reflector’s output). 

In general, ILSpy’s decompiler output does not match the quality of Reflector’s.  However, it appears to produce the cleanest code of the three .NET Reflector alternatives.  Considering it has been in development for less than one month, I think it shows great promise.

Here is the ILSpy decompiler output for the Square and ShapeGroup classes.

namespace Geometry
{
public class Square : Shape
{
  private double _height;
  private double _width;
  public double Height
  {
   get
   {
    return this._height;
   }
   set
   {
    this._height = value;
    return;
   }
  }
  public double Width
  {
   get
   {
    return this._width;
   }
   set
   {
    this._width = value;
    return;
   }
  }
  public override double GetArea()
  {
   return this._height * this._width;
  }
}
}

namespace Geometry
{
public class ShapeGroup
{
  private static class <GetShapeArea>o__SiteContainer2
  {
   public static CallSite<Func<CallSite, object, double>> <>p__Site3;
   public static CallSite<Func<CallSite, object, object>> <>p__Site4;
  }
  private List<IShape> _shapes;
  public List<IShape> Shapes
  {
   get
   {
    return this._shapes;
   }
  }
  public ShapeGroup()
  {
   this._shapes = new List<IShape>();
   base..ctor();
  }
  public void Add(IShape shape)
  {
   this._shapes.Add(shape);
  }
  public void Clear()
  {
   this._shapes.Clear();
  }
  public double GetTotalArea()
  {
   IEnumerable<IShape> arg_23_0 = this._shapes;
   if (ShapeGroup.CS$<>9__CachedAnonymousMethodDelegate1 == null)
   {
    ShapeGroup.CS$<>9__CachedAnonymousMethodDelegate1 = ((IShape n) => n.GetArea());
   }
   return Enumerable.Sum(Enumerable.Select<IShape, double>(arg_23_0, ShapeGroup.CS$<>9__CachedAnonymousMethodDelegate1));
  }
  public double GetShapeArea(int index)
  {
   object item = this._shapes.get_Item(index);
   if (ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site3 == null)
   {
    ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site3 = CallSite<Func<CallSite, object, double>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(double), typeof(ShapeGroup)));
   }
   Func<CallSite, object, double> arg_95_0 = ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site3.Target;
   CallSite arg_95_1 = ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site3;
   if (ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site4 == null)
   {
    CSharpBinderFlags arg_71_0 = CSharpBinderFlags.None;
    string arg_71_1 = "GetArea";
    IEnumerable<Type> arg_71_2 = null;
    Type arg_71_3 = typeof(ShapeGroup);
    CSharpArgumentInfo[] array;
    (array = new CSharpArgumentInfo[1])[0] = CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null);
    ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site4 = CallSite<Func<CallSite, object, object>>.Create(Binder.InvokeMember(arg_71_0, arg_71_1, arg_71_2, arg_71_3, array));
   }
   return arg_95_0.Invoke(arg_95_1, ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site4.Target.Invoke(ShapeGroup.<GetShapeArea>o__SiteContainer2.<>p__Site4, item));
  }
}
}

 

Conclusions

Having evaluated these tools, my view is that if you are a frequent user of .NET Reflector and can’t live without it, go ahead and pay for the tool.  You’ve probably gotten your money’s worth already anyway.

If you use Reflector only occasionally, and particularly if you don’t have an immediate need for it, give the tools I’ve highlighted a try first… particularly ILSpy.  But be prepared to pay for Reflector in case these tools do not meet your need. 

The final word: keep an eye on the ongoing development of these tools.  While Reflector has the edge today, over time it’s certainly possible that one or more of them will become a reliable replacement for .NET Reflector.

Log Parser Rocks! More than 50 Examples!

Log Parser is a tool that has been around for quite some time (almost six years, in fact).  I can’t really do any better than the description on the official download page, so here it is: “Log parser is a powerful, versatile tool that provides universal query access to text-based data such as log files, XML files and CSV files, as well as key data sources on the Windows operating system such as the Event Log, the Registry, the file system, and Active Directory”.  

Log Parser is a command line (yes, command line!) tool that uses a SQL dialect to extract information from data sources.  In particular, I have found it to be invaluable for extracting information from the web server logs of the sites that I manage and develop.

First, about that SQL syntax Log Parser uses to query the data sources… many developers seem to have a natural aversion to SQL.  In addition, many new data access frameworks attempt to abstract SQL away from the developer.  However, I have always found SQL easy to work with and believe it to be an essential tool that every developer should at least have a working knowledge of.   For Log Parser, all that is necessary is a basic understanding of the core SQL SELECT statement, as implemented within Microsoft’s SQL Server (that is, T-SQL).  That means you should be familiar with the following elements of a SELECT statement: TOP, FROM, INTO, WHERE, ORDER BY, GROUP BY.  That’s all you need to perform most Log Parser operations.

Curiously, Log Parser has never received the amount of attention that I think it deserves.  Beyond a flurry of attention when it was first released, it seems to be mentioned rarely in official Microsoft communications or blogs.  Despite that, it remains a viable and valuable tool for parsing not just web server log files, but all types of structured text-based data.

In this post, rather than explaining how to use Log Parser. I’ll give a number of examples of its use.  In addition, I’ll document some useful locations where Log Parser information can be found on the web.

Examples

Keep in mind that most of the examples that I give here are all-in-one command line queries (even though many wrap to multiple lines when displayed here).  However, queries can also be run as

logparser file:XXXXX.sql

where XXXXX is the name of a file containing a logparser-friendly sql query.  There are a couple examples of this in the following list.

The examples given here have been obtained from a variety of sources, including the documentation that ships with the tool, blogs and online documentation, and my own experience.  Unfortunately, I don’t have a record of the origin of each individual example, as I’ve compiled these piecemeal over the last two or three years.

I hope you’ll find something useful here and gain an appreciation for just how robust this tool is.

1)  All pages hits by a given IP address

logparser "select cs-uri-stem, count(cs-uri-stem) as requestcount from [LogFileName] where c-ip = ’000.00.00.000′ group by cs-uri-stem order by count(cs-uri-stem) desc"

2) Hits on a particular page by IP address

logparser "select c-ip, count(c-ip) as requestcount from [LogFileName] where cs-uri-stem like ‘/search.aspx%’ group by c-ip order by count(c-ip) desc"

3)  ReverseDNS example.  This attempts to find the domain associated with a given IP address.

logparser "select c-ip, REVERSEDNS(c-ip) from [LogFileName] where c-ip = ’000.00.00.000′ group by c-ip"

4)  CSV example. All hits on a page, written to a CVS file.

logparser "select * into OUTPUT.CSV from [LogFileName] where cs-uri-stem like ‘/pagename.aspx’"

5)  Chart example.  All hits on a page by an IP address, displayed on a chart.

logparser "select c-ip, count(c-ip) as requestcount into logparserchart.gif from [LogFileName] where cs-uri-stem like ‘/pagename.aspx’ group by c-ip order by count(c-ip) desc" -o:chart

6)  Hits per hour from a particular IP address

logparser "select TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date, time), 3600)), count(*) as numberrequests from [LogFileName] where c-ip=’000.000.00.000′ group by TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date,time), 3600))"

7)  Basic list of IP addresses generating traffic

logparser "select c-ip, count(c-ip) as requestcount from [LogFileName] group by c-ip order by count(c-ip) desc"

8)  Basic list of pages being hit

logparser "select cs-uri-stem, count(cs-uri-stem) from [LogFileName] where cs-uri-stem like ‘%aspx%’ or cs-uri-stem like ‘%ashx%’ group by cs-uri-stem order by count(cs-uri-stem) desc"

9)  Basic list of pages being hit, including which IPs are doing the hitting

logparser "select cs-uri-stem, c-ip, count(cs-uri-stem) from [LogFileName] where cs-uri-stem like ‘%aspx%’ or cs-uri-stem like ‘%ashx%’ group by cs-uri-stem, c-ip order by count(cs-uri-stem) desc"

10)  Pages being hit after a specific date and time

logparser "select cs-uri-stem, c-ip, count(cs-uri-stem) from [LogFileName] where cs-uri-stem like ‘%aspx%’ or cs-uri-stem like ‘%ashx%’ and date=’2009-06-04′ and time > ’15:00:00′ group by cs-uri-stem, c-ip order by count(cs-uri-stem) desc"

11)  Counts of hits of ASPX/ASHX pages by hour from a particular IP address

logparser "select TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date, time), 3600)), count(*) as numberrequests from [LogFileName] where c-ip=’000.000.00.00′ and (cs-uri-stem like ‘%aspx%’ or cs-uri-stem like ‘%ashx%’) group by TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date,time), 3600))"

12)  Counts of hits against specific pages by hour from a particular IP address

logparser "select TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date, time), 3600)), cs-uri-stem, count(*) as numberrequests from [LogFileName] where c-ip=’000.000.00.00′ and (cs-uri-stem like ‘%aspx%’ or cs-uri-stem like ‘%ashx%’) group by TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date,time), 3600)), cs-uri-stem order by numberrequests desc"

13)  Top browsers

logparser "Select top 50 to_int(mul(100.0,PropCount(*))) as Percent, count(*) as TotalHits, cs(User-Agent) as Browser from [LogFileName] group by Browser order by Totalhits desc"

14)  Hourly Bandwidth (chart)

logparser "Select TO_LOCALTIME(QUANTIZE(TO_TIMESTAMP(date, time), 3600)) As Hour, Div(Sum(cs-bytes),1024) As Incoming(K), Div(Sum(sc-bytes),1024) As Outgoing(K) Into BandwidthByHour.gif From [LogFileName] Group By Hour"

15)  Requests by URI

logparser "SELECT top 80 QUANTIZE(TO_TIMESTAMP(date, time), 3600) as Hour, TO_LOWERCASE(STRCAT(‘/’,EXTRACT_TOKEN(cs-uri-stem,1,’/'))) as URI, COUNT(*) AS RequestsPerHour, SUM(sc-bytes) AS TotBytesSent, AVG(sc-bytes) AS AvgBytesSent, Max(sc-bytes) AS MaxBytesSent, ADD(1,DIV(Avg(time-taken),1000)) AS AvgTime, ADD(1,DIV(MAX(time-taken),1000)) AS MaxTime FROM [LogFileName] GROUP BY Hour, URI Having RequestsPerHour > 10 ORDER BY RequestsPerHour ASC"

16)  Top 10 Images by size

logparser "Select Top 10 StrCat(Extract_Path(TO_Lowercase(cs-uri-stem)),’/') AS RequestedPath, Extract_filename(To_Lowercase(cs-uri-stem)) As RequestedFile, Count(*) AS Hits, Max(time-taken) As MaxTime, Avg(time-taken) As AvgTime, Max(sc-bytes) As BytesSent From [LogFileName] Where (Extract_Extension(To_Lowercase(cs-uri-stem)) IN (‘gif’;'jpg’;'png’)) AND (sc-status = 200) Group By To_Lowercase(cs-uri-stem) Order By BytesSent, Hits, MaxTime DESC"

17)  Top 10 URLs for a website, with total hits, max time to serve, and average time to serve

logparser "Select TOP 10 STRCAT(EXTRACT_PATH(cs-uri-stem),’/') AS RequestPath, EXTRACT_FILENAME(cs-uri-stem) AS RequestedFile, COUNT(*) AS TotalHits, Max(time-taken) AS MaxTime, AVG(time-taken) AS AvgTime, AVG(sc-bytes) AS AvgBytesSent FROM [LogFileName] GROUP BY cs-uri-stem ORDER BY TotalHits DESC"

18)  Top 20 clients

logparser "Select Top 20 c-ip AS Client, Count(*) AS Hits INTO Chart.gif FROM [LogFileName] GROUP BY c-ip ORDER BY Hits Desc"

19)  Referrer Broken Links (i.e. external references to broken links on your site)

logparser "SELECT DISTINCT cs(Referer) as Referer, cs-uri-stem as Url INTO ReferBrokenLinks.html FROM [LogFileName] WHERE cs(Referer) IS NOT NULL AND sc-status = 404 AND (sc-substatus IS NULL OR sc-substatus=0)" -tpl:ReferBrokenLinks.tpl

20)  Status codes

logparser "SELECT sc-status As Status, COUNT(*) As Number INTO StatusCodes.gif FROM <2> GROUP BY Status ORDER BY Status"

21)  Search the Event Log for W3SVC (IIS) log entries and color-coordinate as to Error, Warning, Information.  This example writes the output of the query to an HTML file that  is generated using a template file.

logparser "SELECT TimeGenerated,EventTypeName,Strings,Message,CASE EventTypeName WHEN ‘Error event’ THEN ‘RED’ WHEN ‘Warning event’ THEN ‘YELLOW’ WHEN ‘Information event’ THEN ‘WHITE’ ELSE ‘BLUE’ END As Color INTO file.html FROM System WHERE SourceName = ‘W3SVC’"  -tpl:IISEventLogEntries.tpl

Where IISEventLogEntries.tpl is a file that contains the following:

<LPHEADER>
<HTML>
<HEAD>
  <STYLE>
    TD { font-family: Arial };
    TH { font-family: Arial };
  </STYLE>
</HEAD>
<BODY>
<TABLE BORDERCOLOR="BLACK" BORDER="1" CELLPADDING="2" CELLSPACING="2">
<TR>
  <TH COLSPAN=4 BGCOLOR="BLACK"><FONT COLOR=WHITE>New W3SVC Messages in System Event Log</FONT></TH>
</TR>
<TR>
  <TH ALIGN=LEFT BGCOLOR="#C0C0C0">Time Generated</TH>
  <TH ALIGN=LEFT BGCOLOR="#C0C0C0">Event Type</TH>
  <TH ALIGN=LEFT BGCOLOR="#C0C0C0">Strings</TH>
  <TH ALIGN=LEFT BGCOLOR="#C0C0C0">Message</TH>
</TR>
</LPHEADER>
<LPBODY>
<TR bgCOLOR="%Color%">
  <TD>%TimeGenerated%</TD>
  <TD>%EventTypeName%</TD>
  <TD>%Strings%</TD>
  <TD>%Message%</TD>
</TR>
</LPBODY>
</TABLE>
</BODY>
</HTML>

22)  Upload Log Parser query results directly to a table in SQL Server

logparser "select * into LogTable from [LogFileName] where cs-uri-stem like ‘/folder/filename%’" -o:SQL -createTable:ON -server:[DatabaseServer] -database:[Database] -username:[SqlUser] -password:[SqlPassword]

23)  Top 10 images by size sent.  Note that this example also shows how to query multiple log files at once.

logparser "Select Top 10 StrCat(Extract_Path(TO_Lowercase(cs-uri-stem)),’/') AS RequestedPath, Extract_filename(To_Lowercase(cs-uri-stem)) As RequestedFile, Count(*) AS Hits, Max(time-taken) As MaxTime, Avg(time-taken) As AvgTime, Max(sc-bytes) As BytesSent INTO TOP10ImagesBySize.txt FROM logs\iis\ex*.log WHERE (Extract_Extension(To_Lowercase(cs-uri-stem)) IN  (‘gif’;'jpg’;'png’)) AND (sc-status = 200) GROUP BY To_Lowercase(cs-uri-stem) ORDER BY BytesSent, Hits, MaxTime DESC"

24)  Browser types (two different approaches)

logparser "SELECT distinct cs(User-Agent), count(*) as hits INTO useragentsalltypes.txt FROM logs\iis\ex*.log GROUP BY cs(user-agent) ORDER BY hits DESC"

logparser "SELECT TO_INT(MUL(100.0,PROPCOUNT(*))) AS Percent,  COUNT(*) AS Hits, cs(User-Agent) as Browser INTO  UseragentsHits.txt FROM  logs\iis\ex*.log  GROUP BY Browser ORDER BY HITS DESC"

25)  Unique visitors per day.  This requires two queries.  The first query selects from the IIS logs into a CSV file, and the second selects from that CSV file.

logparser "SELECT DISTINCT cs-username, date INTO tempUniqueVisitorsPerDay.csv FROM logs\iis\ex*.log WHERE cs-username <> NULL Group By Date, cs-username"

logparser "SELECT date, count(cs-username) as UniqueVisitors into test.txt FROM tempUniqueVisitorsPerDay.csv GROUP BY date"

26)  Top 10 largest ASPX pages.

logparser "Select Top 10 StrCat(Extract_Path(TO_Lowercase(cs-uri-stem)),’/') AS  RequestedPath, Extract_filename(To_Lowercase(cs-uri-stem)) As RequestedFile,  Count(*) AS Hits, Max(time-taken) As MaxTime, Avg(time-taken) As AvgTime, Max(sc-bytes) As BytesSent INTO top10pagesbysize.txt FROM logs\iis\ex*.log WHERE (Extract_Extension(To_Lowercase(cs-uri-stem)) IN (‘aspx’)) AND  (sc-status = 200) GROUP BY To_Lowercase(cs-uri-stem) ORDER BY BytesSent, Hits, MaxTime DESC"

27)  Top 10 slowest ASPX pages

logparser "SELECT TOP 10 cs-uri-stem, max(time-taken) as MaxTime, avg(time-taken) as AvgTime INTO toptimetaken.txt FROM logs\iis\ex*.log WHERE extract_extension(to_lowercase(cs-uri-stem)) = ‘aspx’ GROUP BY cs-uri-stem ORDER BY MaxTime DESC"

28)  Top 10 slowest ASPX pages on a specific day

logparser "SELECT TOP 10 cs-uri-stem, max(time-taken) as MaxTime, avg(time-taken) as AvgTime INTO toptimetaken.txt FROM logs\iis\ex*.log WHERE extract_extension(to_lowercase(cs-uri-stem)) = ‘aspx’ AND TO_STRING(To_timestamp(date, time), ‘MMdd’)=’1003′  GROUP BY cs-uri-stem ORDER BY MaxTime DESC"

29)  Daily bandwidth

logparser "Select To_String(To_timestamp(date, time), ‘MM-dd’) As Day, Div(Sum(cs-bytes),1024) As Incoming(K), Div(Sum(sc-bytes),1024) As Outgoing(K) Into BandwidthByDay.gif From logs\iis\ex*.log Group By Day"

30)  Bandwidth by hour

logparser "SELECT QUANTIZE(TO_TIMESTAMP(date, time), 3600) AS Hour, SUM(sc-bytes) AS TotalBytesSent INTO BytesSentPerHour.gif FROM logs\iis\ex*.log GROUP BY Hour ORDER BY Hour"

31)  Average page load time per user

logparser "Select Top 20 cs-username AS UserName, AVG(time-taken) AS AvgTime,  Count(*) AS Hits INTO AvgTimePerUser.txt FROM logs\iis\ex*.log WHERE cs-username IS NOT NULL GROUP BY cs-username ORDER BY AvgTime DESC"

32)  Ave page load time for a specific user

logparser "Select cs-username AS UserName, AVG(time-taken) AS AvgTime,  Count(*) AS Hits INTO AvgTimeOnSpecificUser.txt FROM logs\iis\ex*.log WHERE cs-username = ‘CONTOSO\User1234’ GROUP BY cs-username"

33)  Error trends.  This query is quite long, and is easier expressed in a text file than on the command line.  So, Log Parser reads and executes the query contained in the specified text file.

logparser file:errortrend.sql

Where errortrend.sql contains the following:

SELECT
  TO_STRING(To_timestamp(date, time), ‘MMdd’) AS Day,
  SUM(c200) AS 200s,
  SUM(c206) AS 206s,
  SUM(c301) AS 301s,
  SUM(c302) AS 302s,
  SUM(c304) AS 304s,
  SUM(c400) AS 400s,
  SUM(c401) AS 401s,
  SUM(c403) AS 403s,
  SUM(c404) AS 404s,
  SUM(c500) AS 500s,
  SUM(c501) AS 501s,
  SUM(c502) AS 502s,
  SUM(c503) AS 503s,
  SUM(c504) AS 504s,
  SUM(c505) AS 505s
USING
  CASE sc-status WHEN 200 THEN 1 ELSE 0 END AS c200,
  CASE sc-status WHEN 206 THEN 1 ELSE 0 END AS c206,
  CASE sc-status WHEN 301 THEN 1 ELSE 0 END AS c301,
  CASE sc-status WHEN 302 THEN 1 ELSE 0 END AS c302,
  CASE sc-status WHEN 304 THEN 1 ELSE 0 END AS c304,
  CASE sc-status WHEN 400 THEN 1 ELSE 0 END AS c400,
  CASE sc-status WHEN 401 THEN 1 ELSE 0 END AS c401,
  CASE sc-status WHEN 403 THEN 1 ELSE 0 END AS c403,
  CASE sc-status WHEN 404 THEN 1 ELSE 0 END AS c404,
  CASE sc-status WHEN 500 THEN 1 ELSE 0 END AS c500,
  CASE sc-status WHEN 501 THEN 1 ELSE 0 END AS c501,
  CASE sc-status WHEN 502 THEN 1 ELSE 0 END AS c502,
  CASE sc-status WHEN 503 THEN 1 ELSE 0 END AS c503,
  CASE sc-status WHEN 504 THEN 1 ELSE 0 END AS c504,
  CASE sc-status WHEN 505 THEN 1 ELSE 0 END AS c505
INTO ErrorChart.gif
FROM
    logs\iis\ex*.log
GROUP BY
  Day
ORDER BY
  Day

34)  Win32 errors

logparser "SELECT sc-win32-status as ErrorNumber, WIN32_ERROR_DESCRIPTION(sc-win32-status) as ErrorDesc, Count(*) AS Total INTO Win32ErrorNumbers.txt FROM logs\iis\ex*.log WHERE sc-win32-status>0 GROUP BY ErrorNumber ORDER BY Total DESC"

35)  Substatus codes

logparser "SELECT sc-status, sc-substatus, Count(*) AS Total INTO 401subcodes.txt FROM logs\iis\ex*.log WHERE sc-status=401 GROUP BY sc-status, sc-substatus ORDER BY sc-status, sc-substatus DESC"

36)  Substatus codes per day.  This is another example of executing a query contained in a text file.

logparser file:substatusperday.sql

Where substatusperday.sql contains the following:

SELECT
  TO_STRING(To_timestamp(date, time), ‘MMdd’) AS Day,
  SUM(c1) AS 4011,
  SUM(c2) AS 4012,
  SUM(c3) AS 4013,
  SUM(c4) AS 4014,
  SUM(c5) AS 4015,
  SUM(c7) AS 4017
USING
  CASE sc-substatus WHEN 1 THEN 1 ELSE 0 END AS c1,
  CASE sc-substatus WHEN 2 THEN 1 ELSE 0 END AS c2,
  CASE sc-substatus WHEN 3 THEN 1 ELSE 0 END AS c3,
  CASE sc-substatus WHEN 4 THEN 1 ELSE 0 END AS c4,
  CASE sc-substatus WHEN 5 THEN 1 ELSE 0 END AS c5,
  CASE sc-substatus WHEN 7 THEN 1 ELSE 0 END AS c7
INTO
  401subcodesperday.txt
FROM
  logs\iis\ex*.log
WHERE
  sc-status=401
GROUP BY
  Day
ORDER BY
  Day

37)  Substatus codes per page

logparser "SELECT TOP 20 cs-uri-stem, sc-status, sc-substatus, Count(*) AS Total INTO 401Pagedetails.txt FROM logs\iis\ex*.log WHERE sc-status=401 GROUP BY cs-uri-stem, sc-status, sc-substatus ORDER BY Total"

38)  MB sent per HTTP status code

logparser "SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, SUM(sc-bytes) as TotalBytesSent, TO_INT(MUL(PROPSUM(sc-bytes), 100.0)) AS PercentBytes INTO PagesWithLargestBytesSent.htm FROM logs\iis\ex*.log GROUP BY Pagetype ORDER BY PercentBytes DESC"

39) 500 errors per ASPX and Domain User

logparser "SELECT cs-username, cs-uri-stem, count(*) as Times INTO 500PagesByUserAndPage.txt FROM logs\iis\ex*.log WHERE sc-status=500 GROUP BY  cs-username, cs-uri-stem ORDER BY Times DESC"

40)  Percent of 500 errors caused by each user

logparser "SELECT cs-username, count(*) as Times, propcount(*) as Percent INTO 500ErrorsByUser.csv FROM  logs\iis\ex*.log WHERE sc-status=500 GROUP BY cs-username ORDER BY Times DESC"

41)  Determine what percentage of the total bytes sent are being caused by each page type

logparser "SELECT EXTRACT_EXTENSION(cs-uri-stem) AS PageType, SUM(sc-bytes) as TotalBytesSent, TO_INT(MUL(PROPSUM(sc-bytes), 100.0)) AS PercentBytes INTO PagesWithLargestBytesSent.txt FROM logs\iis\ex*.log GROUP BY Pagetype ORDER BY PercentBytes DESC"

42)  Top 20 pages with a specific HTTP return code

logparser "SELECT TOP 20 cs-uri-stem, sc-status, Count(*) AS Total INTO TOP20PagesWith401.txt FROM logs\iis\ex*.log WHERE TO_LOWERCASE(cs-uri-stem) LIKE ‘%.aspx’ and sc-status=401 GROUP BY cs-uri-stem, sc-status ORDER BY Total, cs-uri-stem, sc-status DESC"

43)  Check traffic from IP addresses

logparser "Select c-ip AS Client, Div(Sum(cs-bytes),1024) As IncomingBytes(K), Div(Sum(sc-bytes),1024) As OutgoingBytes(K), MAX(time-taken) as MaxTime, AVG(time-taken) as AvgTime, count(*) as hits INTO errorsperip.txt FROM logs\iis\ex*.log GROUP BY client ORDER BY Hits DESC"

44)  Check errors by IP address

logparser file:errorbyip.sql

Where errorbyip.sql contains the following:

Select
  c-ip AS Client,
  SUM(c400) AS 400s,
  sum(c401) AS 401s,
  SUM(c403) AS 403s,
  SUM(c404) AS 404s,
  SUM(c500) AS 500s,
  SUM(c501) AS 501s,
  SUM(c502) AS 502s,
  SUM(c503) AS 503s,
  SUM(c504) AS 504s,
  SUM(c505) AS 505s
USING
  CASE sc-status WHEN 400 THEN 1 ELSE 0 END AS c400,
  CASE sc-status WHEN 401 THEN 1 ELSE 0 END AS c401,
  CASE sc-status WHEN 403 THEN 1 ELSE 0 END AS c403,
  CASE sc-status WHEN 404 THEN 1 ELSE 0 END AS c404,
  CASE sc-status WHEN 500 THEN 1 ELSE 0 END AS c500,
  CASE sc-status WHEN 501 THEN 1 ELSE 0 END AS c501,
  CASE sc-status WHEN 502 THEN 1 ELSE 0 END AS c502,
  CASE sc-status WHEN 503 THEN 1 ELSE 0 END AS c503,
  CASE sc-status WHEN 504 THEN 1 ELSE 0 END AS c504,
  CASE sc-status WHEN 505 THEN 1 ELSE 0 END AS c505
INTO
  IPNumberFileName.txt
FROM
    logs\iis\ex*.log
WHERE
    c-ip=’<IP address goes here>’
GROUP BY
    client

45)  Find broken links

logparser "SELECT DISTINCT cs(Referer) as Referer, cs-uri-stem as Url INTO ReferBrokenLinks.txt FROM logs\iis\ex*.log WHERE cs(Referer) IS NOT NULL AND sc-status=404 AND (sc-substatus IS NULL OR sc-substatus=0)"

46)  Top 10 pages with most hits

logparser "Select TOP 10 STRCAT(EXTRACT_PATH(cs-uri-stem),’/') AS RequestPath, EXTRACT_FILENAME(cs-uri-stem) AS RequestedFile, COUNT(*) AS TotalHits, Max(time-taken) AS MaxTime, AVG(time-taken) AS AvgTime, AVG(sc-bytes) AS AvgBytesSent INTO Top10Urls.txt FROM logs\iis\ex*.log GROUP BY cs-uri-stem ORDER BY TotalHits DESC"

47)  Unique users per browser type (requires two queries)

logparser "SELECT DISTINCT cs-username, cs(user-agent) INTO UserAgentsUniqueUsers1.csv FROM logs\iis\ex*.log WHERE cs-username <> NULL GROUP BY cs-username, cs(user-agent)"

logparser "SELECT cs(user-agent), count(cs-username) as UniqueUsersPerAgent, TO_INT(MUL(PROPCOUNT(*), 100)) AS Percentage INTO UniqueUsersPerAgent.txt FROM UserAgentsUniqueUsers1.csv GROUP BY  cs(user-agent) ORDER BY UniqueUsersPerAgent DESC"

48)  Bytes sent per file extension

logparser "SELECT EXTRACT_EXTENSION( cs-uri-stem ) AS Extension, MUL(PROPSUM(sc-bytes),100.0) AS PercentageOfBytes, Div(Sum(sc-bytes),1024) as AmountOfMbBytes INTO BytesPerExtension.txt FROM logs\iis\ex*.log GROUP BY Extension ORDER BY PercentageOfBytes DESC"

49)  Domains referring traffic to your site

logparser "SELECT EXTRACT_TOKEN(cs(Referer), 2, ‘/’) AS Domain, COUNT(*) AS [Requests] INTO ReferringDomains.txt FROM  logs\iis\ex*.log GROUP BY Domain ORDER BY Requests DESC"

50)  OS types (requires two queries)

logparser "SELECT DISTINCT c-ip, cs(user-agent) INTO UserAgentsUniqueUsers.csv FROM logs\iis\ex*.log WHERE c-ip <> NULL GROUP BY c-ip, cs(user-agent)"

logparser file:getos.sql

Where getos.sql contains the following:

SELECT
  SUM (c70) AS Win7,
  SUM (c60) AS Vista,
  SUM (c52) AS Win2003,
  SUM (c51) AS WinXP,
  SUM (C50) AS Win2000,
  SUM (W98) AS Win98,
  SUM (W95) AS Win95,
  SUM (W9x) AS Win9x,
  SUM (NT4) AS WinNT4,
  SUM (OSX) AS OS-X,
  SUM (Mac) AS Mac-,
  SUM (PPC) AS Mac-PPC,
  SUM (Lnx) AS Linux
USING
  CASE strcnt(cs(User-Agent),’Windows+NT+6.1′) WHEN 1 THEN 1 ELSE 0 END AS C70,
  CASE strcnt(cs(User-Agent),’Windows+NT+6.0′) WHEN 1 THEN 1 ELSE 0 END AS C60,
  CASE strcnt(cs(User-Agent),’Windows+NT+5.2′) WHEN 1 THEN 1 ELSE 0 END AS C52,
  CASE strcnt(cs(User-Agent),’Windows+NT+5.1′) WHEN 1 THEN 1 ELSE 0 END AS C51,
  CASE strcnt(cs(User-Agent),’Windows+NT+5.0′) WHEN 1 THEN 1 ELSE 0 END AS C50,
  CASE strcnt(cs(User-Agent),’Win98′) WHEN 1 THEN 1 ELSE 0 END AS W98,
  CASE strcnt(cs(User-Agent),’Win95′) WHEN 1 THEN 1 ELSE 0 END AS W95,
  CASE strcnt(cs(User-Agent),’Win+9x+4.90′) WHEN 1 THEN 1 ELSE 0 END AS W9x,
  CASE strcnt(cs(User-Agent),’Winnt4.0′) WHEN 1 THEN 1 ELSE 0 END AS NT4,
  CASE strcnt(cs(User-Agent),’OS+X’) WHEN 1 THEN 1 ELSE 0 END AS OSX,
  CASE strcnt(cs(User-Agent),’Mac’) WHEN 1 THEN 1 ELSE 0 END AS Mac,
  CASE strcnt(cs(User-Agent),’PPC’) WHEN 1 THEN 1 ELSE 0 END AS PPC,
  CASE strcnt(cs(User-Agent),’Linux’) WHEN 1 THEN 1 ELSE 0 END AS Lnx
INTO
  GetOSUsed.txt
FROM
  UserAgentsUniqueUsers.csv

51)  Get timeout errors from the server Event Log.  Display results in a datagrid.

logparser "select * from \\servername\application where message like ‘%timeout expired%’" -i:EVT -o:datagrid

52)  Get exceptions from the server Event (Application) Log

logparser "select timegenerated, eventtypename, eventcategoryname, message into webserverlog.csv from \\servername\application where message like ‘%myapplication%exception%’" -i:EVT

Links

Check out the links below to find more in-depth discussion of Log Parser, as well as even more examples of its usage.

    Follow

    Get every new post delivered to your Inbox.