I have a logging system set up in my C# WinForms project that writes to a log txt file. A typical error message looks like this:
Error :
8:34:48 AM Tuesday, April 21, 2020
:
:System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.Collections.ArrayList.get_Item(Int32 index)
at System.Windows.Forms.DataGridViewColumnCollection.get_Item(Int32 index)
at CrossReferenceTool.frmXRefTool.DefineControlsLayout() in <PathToSourceCsFile>:line 306
Is there any way to grab parts of that error message? Specifically, I'd like to pull the offending method (DefineControlsLayout() in this case) and the line number.
Instantiate a new StackTrace and go log the details from that after any exception occurs.
Original link I used: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stacktrace.getframe?view=netframework-4.8
Example code
using System.Diagnostics;
try
{
throw new Exception("Fail");
}
catch (Exception e)
{
StackTrace st = new StackTrace(e);
// Display the most recent function call.
StackFrame sf = st.GetFrame(0);
Console.WriteLine();
Console.WriteLine(" Exception in method: ");
Console.WriteLine(" {0}", sf.GetMethod());
if (st.FrameCount > 1)
{
// Display the highest-level function call
// in the trace.
sf = st.GetFrame(st.FrameCount - 1);
Console.WriteLine(" Original function call at top of call stack):");
Console.WriteLine(" {0}", sf.GetMethod());
// will only work if .pdb is included
var lineNumber = sf.GetFileLineNumber();
var fileName = sf.GetFileName();
}
}
If you are already catching an Exception type then you have sub properties such as
Exception.StackTrace
Exception.TargetSite
I don't think that you can use them to pull the line of code though.
Related
I want to log error in a web application designed in asp.net webforms.
Currently i use following code to log error when ever exception is raised
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
Exception exc = Server.GetLastError();
// Include enterprise logic for logging exceptions
// Get the absolute path to the log file
string logFile = "~/App_Data/ErrorLog.txt";
logFile = HttpContext.Current.Server.MapPath(logFile);
// Open the log file for append and write the log
using (StreamWriter sw = new StreamWriter(logFile, true))
{
sw.WriteLine("********** {0} **********", DateTime.Now);
if (exc.InnerException != null)
{
sw.Write("Inner Exception Type: ");
sw.WriteLine(exc.InnerException.GetType().ToString());
sw.Write("Inner Exception: ");
sw.WriteLine(exc.InnerException.Message);
//sw.Write("Inner Source: ");
sw.WriteLine(exc.InnerException.Source);
if (exc.InnerException.StackTrace != null)
{
// sw.WriteLine("Inner Stack Trace: ");
// sw.WriteLine(exc.InnerException.StackTrace);
}
}
sw.Write("Exception Type: ");
sw.WriteLine(exc.GetType().ToString());
sw.WriteLine("Exception: " + exc.Message);
// sw.WriteLine("Source: " + source);
//sw.WriteLine("Stack Trace: ");
if (exc.StackTrace != null)
{
// sw.WriteLine(exc.StackTrace);
// sw.WriteLine();
}
// sw.Close();
}
}
How can i modify this code so that i can check the file size first to see if it has reached 1MB size. If logfile has reached the 1MB size then i will create another file with date label etc.
You can use FileInfo in this way:
FileInfo fi = new FileInfo(logFile);
if(fi.length > 1024) {
// create new file
}
Another option is to use a powerful logging library such as NLog. It allows you to specify maximum file size for a log file and also to automatically delete old files.
As you can see, these options and many other are automatically handled based on a xml configuration.
I'm doing an activity that asks your name, and if a number is inserted it should tell that the input is invalid.
Here's my code:
try {
Console.Write("Enter your first name: ");
string fname = Console.ReadLine();
Console.Write("You're Name is: " + fname);
}
catch (Exception) {
Console.Write("INVALID NAME");
}
Sample output:
Enter you're first name: 123hjay
INVALID NAME!!
I know my exception is wrong; I need your help guys.
You seem to have misunderstood the purpose of exceptions.
Exceptions are thrown when a program encounters an error in its execution. For example assigning a letter to an int would throw an error. While opinions vary, I tend not to handle user input errors with exceptions. Furthermore, think about the logic you wrote in your code. How could the program know that entering numbers into a variable named fname is incorrect?
Write in logic into your program to test for input errors and then return an appropriate response. In your case, if you wanted to ensure that there were no numbers entered, you could do the following:
if (name.Any(char.IsNumber))
{
Console.WriteLine("Invalid Name.");
}
Console.ReadLine();
As my comment says (in the question), I didn't really get what it is you are asking, because it doesn't throw anything, but if you did want it to throw an error (also suggested by the comments), this should help:
Console.Write("Enter you're first name: ");
string fname = Console.ReadLine();
foreach (var character in fname)
{
if (Char.IsDigit(character))
throw new Exception("Numbers are not allowed.");
}
Console.Write("You're Name is: " + fname);
It's very straight forward and you can read it as English and understand what I've done.
You can mess around with the code, look at similar functions with Visual Studios IntelliSense and tweak it to your needs.
If you need, add a try & catch blocks, of course.
for the courtesy of replying back, here is the answer.
Hint: your exception is right!
Enter in your console "Glee" it will not throw exception.
If you type "7337" it does throw exception.
try to use : fname.ToString();
I want to have a custom error page that displays some error information, but not the stack trace.
I would like for it to display the information for the offending page and the line number.
The default SERVER ERROR IN APPLICATION page shows the url/line number and surrounding code/stack trace.
I just want the url/line number to appear somewhere inside a prettier custom error page. That way our code isn't exposed when an error is thrown, but I can still find the error quickly by url/line number.
I already have custom error pages turned on. I just want to add additional information to them.
I'm using C#.NET 4.0 and webforms.
This is a question I have had for a while and for the longest time though it was not possible, so I did some research and found a solution. You need to use the StackFrame object from the System.Diagnostics namespace. I just put together an example and it appears to have worked.
try
{
int a = 10;
int b = 0;
litDebug.Text = (a / b).ToString();
}
catch (Exception ex)
{
StackTrace st = new StackTrace(ex, true);
StackFrame sf = st.GetFrame(0);
litDebug.Text = "" +
"<br />File Name: " + sf.GetFileName() +
"<br />Method Name: " + sf.GetMethod().Name +
"<br />Error Line Number: " + sf.GetFileLineNumber() +
"<br />Error Column Number: " + sf.GetFileColumnNumber();
}
The page output is:
File Name: c:\inetpub\www.website.com\dev\error.aspx.cs
Method Name: Page_Load
Error Line Number: 17
Error Column Number: 4
The only questionable item is the Error Column Number - everything else matches up perfectly.
How would one display what line number caused the error and is this even possible with the way that .NET compiles its .exes?
If not is there an automated way for Exception.Message to display the sub that crapped out?
try
{
int x = textbox1.Text;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
Use ex.ToString() to get the full stack trace.
You must compile with debugging symbols (.pdb files), even in release mode, to get the line numbers (this is an option in the project build properties).
To see the stacktrace for a given Exception, use e.StackTrace
If you need more detailed information, you can use the System.Diagnostics.StackTrace class (here is some code for you to try):
try
{
throw new Exception();
}
catch (Exception ex)
{
//Get a StackTrace object for the exception
StackTrace st = new StackTrace(ex, true);
//Get the first stack frame
StackFrame frame = st.GetFrame(0);
//Get the file name
string fileName = frame.GetFileName();
//Get the method name
string methodName = frame.GetMethod().Name;
//Get the line number from the stack frame
int line = frame.GetFileLineNumber();
//Get the column number
int col = frame.GetFileColumnNumber();
}
This will only work if there is a pdb file available for the assembly. See the project properties - build tab - Advanced - Debug Info selection to make sure there is a pdb file.
If you use 'StackTrace' and include the .pdb files in the working directory, the stack trace should contain line numbers.
string lineNumber=e.StackTrace.Substring(e.StackTrace.Length - 7, 7);
this way you can Get Line number from Exception
public int GetLineNumber(Exception ex)
{
const string lineSearch = ":line ";
var index = ex.StackTrace.LastIndexOf(lineSearch);
int ln=0;
if (index != -1)
{
var lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length);
string lnum = System.Text.RegularExpressions.Regex.Match(lineNumberText, #"\d+").Value;
int.TryParse(lnum,out ln);
}
return ln;
}
Line numbers will be included in the stack trace if the library which generated the exception is compiled with debug symbols. This can be a separate file (*.pdb) or embedded in the library.
For .NET Core, .NET 5 and later, to have full exception line numbers in release builds, configure the project as follows:
<PropertyGroup>
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<!-- Only enable the following if the line numbers mismatch -->
<!--<Optimize>false</Optimize>-->
<!--
Additional properties which may impact how printed line numbers match the source code line numbers are listed here:
https://learn.microsoft.com/en-us/dotnet/core/run-time-config/compilation
-->
</PropertyGroup>
The above configuration will include debug symbols directly with the built files, which can be published as nugets.
An alternative to the above is to restore debug packages together with the main nuget packages, which is currently not yet supported: https://github.com/NuGet/Home/issues/9667
Now get the exception line numbers:
try
{
throw new Exception();
}
catch (Exception ex)
{
// Get stack trace for the exception with source file information
var st = new StackTrace(ex, true);
// Get the top stack frame
var frame = st.GetFrame(0);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
}
Is it possible to simply get the top frame from the StackTrace exposed by ex?
try
{
throw new Exception();
}
catch (Exception ex)
{
// Get the top stack frame
var frame = ex.StackTrace().GetFrame(0);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
}
For logging purposes
__LINE__
__FILE__
were my friends in C/C++. In Java to get that information I had to throw an exception and catch it. Why are these old standbys so neglected in the modern programming languages? There is something magical about their simplicity.
Caller Information has been added to .NET 4.5. This will be compiled, a big improvement over having to examine the stack trace manually.
public void Log(string message,
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
// Do logging
}
Simply call it in this manner. The compiler will fill in the file name and line number for you:
logger.Log("Hello!");
It is uglier, but you can do something like this in C# using the StackTrace and StackFrame classes:
StackTrace st = new StackTrace(new StackFrame(true));
Console.WriteLine(" Stack trace for current level: {0}", st.ToString());
StackFrame sf = st.GetFrame(0);
Console.WriteLine(" File: {0}", sf.GetFileName());
Console.WriteLine(" Method: {0}", sf.GetMethod().Name);
Console.WriteLine(" Line Number: {0}", sf.GetFileLineNumber());
Console.WriteLine(" Column Number: {0}", sf.GetFileColumnNumber());
Of course, this comes with some overhead.
With Caller Information (introduced in .NET 4.5) you can create the equivalent of __LINE__ and __FILE__ in C#:
static int __LINE__([System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0)
{
return lineNumber;
}
static string __FILE__([System.Runtime.CompilerServices.CallerFilePath] string fileName = "")
{
return fileName;
}
The only thing to remember is that these are functions and not compiler directives.
So for example:
MessageBox.Show("Line " + __LINE__() + " in " + __FILE__());
If you were to use this in practise then I'd suggest different names. I've used the C/C++ names just to make it clearer what they are returning, and something like CurrentLineNumber() and CurrentFileName() might be better names.
The advantage of using Caller Information over any solution that uses the StackTrace is that the line and file information is available for both debug and release.
The closest thing to those is the fact that you can create a StackTrace object and find out the name of the method at the top of the stack, so you can get close to the functionality of the __FUNCTION__ macro.
StackTrace stackTrace = new StackTrace();
StackFrame[] stackFrames = stackTrace.GetFrames();
foreach (StackFrame stackFrame in stackFrames)
Console.WriteLine(stackFrame.GetMethod().Name);
To reduce the cost of typing this out by hand, and also the runtime code, you can write a helper method:
[Conditional("Debug")]
public void LogMethodName()
{
Trace.WriteLine("Entering:" + new StackTrace().GetFrame(1).GetMethod().Name);
}
Note how we get frame 1, as frame 0 would be LogMethodName itself. By marking it as Conditional("Debug") we ensure that the code is removed from release builds, which is one way to avoid the runtime cost where it may not be needed.
Because the stack trace contains most of what you need. It will not give you the name of the file but it will give you the class/method name. It also contains the line number. It is not neglected it is automatic. You just need to throw an exception like you do it in Java
Here's a way to get the line number: http://askville.amazon.com/SimilarQuestions.do?req=line-numbers-stored-stack-trace-C%2523-application-throws-exception
If you use log4net, you can get the line number and file name in your logs, but:
it can decrease your app. performance
you have to have .PDB files together with your assemblies.
There are already some suggestions to achieve what you want. Either use the StackTrace object or better log4net.
In Java to get that information I had to throw an exception and catch it.
That's not quite true. You can have it without throwing exceptions, too. Have a look to log4j. It even logs your method and class name, without polluting your code with hard coded strings containing the current method name (at least I have seen this in some occasions).
Why are these old standbys so neglected in the modern programming languages?
Java and C# don't make use (in the latter: excessive use) of preprocessors. And I think it's good. Abusing preprocessors to make unreadable code is very easy. And if programmers can abuse some technique ... they will abuse it.
Just a note about performance, which is very likely to be the next thing, which pops up in your mind:
If you use StackTrace or log4net you will always will read or hear that it is slow, because it uses Reflection. I am using log4net and I never encountered logging as a performance bottle neck. If it would be, I can declaratively deactivate (parts of) logging -- without changing the source code. That's pure beauty compared to delete all the logging lines in C/C++ code! (Besides: If performance is a primary goal, I would use C/C++ ... it will never die despite of Java and C#.)
Having used the FILE and LINE macros for many years for logging in C/C++ I really wanted a similar solution in C#. This is my solution. I prefer it to #fostandy suggestion of creating many overloads with varying number of parameters. This seems the less intrusive and does not limit the number of formatted parameters. You just have to be willing to accept the addition of the F.L() parameter at start of every Log.Msg() call.
using System;
using System.Runtime.CompilerServices;
namespace LogFileLine
{
public static class F
{
// This method returns the callers filename and line number
public static string L([CallerFilePath] string file = "", [CallerLineNumber] int line = 0)
{
// Remove path leaving only filename
while (file.IndexOf("\\") >= 0)
file = file.Substring(file.IndexOf("\\")+1);
return String.Format("{0} {1}:", file, line);
}
}
public static class Log
{
// Log a formatted message. Filename and line number of location of call
// to Msg method is automatically appended to start of formatted message.
// Must be called with this syntax:
// Log.Msg(F.L(), "Format using {0} {1} etc", ...);
public static void Msg(string fileLine, string format, params object[] parms)
{
string message = String.Format(format, parms);
Console.WriteLine("{0} {1}", fileLine, message);
}
}
class Program
{
static void Main(string[] args)
{
int six = 6;
string nine = "nine";
string dog = "dog";
string cat = "cats";
Log.Msg(F.L(), "The {0} chased the {1} {2}", dog, 5, cat);
Log.Msg(F.L(), "Message with no parameters");
Log.Msg(F.L(), "Message with 8 parameters {0} {1} {2} {3} {4} {5} {6} {7}",
1, 2, 3, "four", 5, 6, 7, 8.0);
Log.Msg(F.L(), "This is a message with params {0} and {1}", six, nine);
}
}
}
Here's the output from this code above
Program.cs 41: The dog chased the 5 cats
Program.cs 43: Message with no parameters
Program.cs 45: Message with 8 parameters 1 2 3 four 5 6 7 8
Program.cs 48: This is a message with params 6 and nine