I am analyzing errors in a legacy application as I clean it up and improve it. I have some stack traces being logged to the database, but there is a limit (VARCHAR2(1000)) to how much it is storing, and errors are occurring deep within System.Data...
at System.Data.OracleClient.OracleConnection.CheckError(OciErrorHandle errorHandle, Int32 rc)
at System.Data.OracleClient.OracleCommand.Execute(OciStatementHandle statementHandle, CommandBehavior behavior, Boolean needRowid, OciRowidDescriptor& rowidDescriptor, ArrayList& resultParameterOrdinals)
at System.Data.OracleClient.OracleCommand.Execute(OciStatementHandle statementHandle, CommandBehavior behavior, ArrayList& resultParameterOrdinals)
at System.Data.OracleClient.OracleCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.OracleClient.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet)
I don't care about all this stuff - I just need to find which C# code and which proc is generating the errors. So I can figure out the root cause, refactor the code to quality and improve each module as I hit the lowest-hanging most-error-producing code.
Before I go off and write something, does someone have something which will limit the stack trace to certain assemblies so I can get to the root cause? Or a stack trace parser or something off the shelf which I can use to strip out the stuff I'm not interested in before logging the error?
You're in luck - somebody who's done that read your post! I'd gather I've saved my eyes from from reading at least a Steven King novel's worth of fluff by reformatting my errors into something like this:
(<pre> didn't honor my tabstops so it looks better in real life)
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
FUNCTION FILE LINE
CoCreateInstance (no .PDB)
CreateWithoutLicense (no .PDB)
CreateWithLicense (no .PDB)
CreateInstanceCore (no .PDB)
CreateInstance (no .PDB)
GetOcxCreate (no .PDB)
TransitionUpTo (no .PDB)
CreateHandle (no .PDB)
CreateControl (no .PDB)
CreateControl (no .PDB)
EndInit (no .PDB)
InitializeComponent frmGetSig.Designer.vb 187
ctor frmGetSig.vb 264
btnSign_Click ucSignCapture_v2.vb 306
Using this code:
Public Shared Function ReduceError(ByVal stExceptionToString As String) As String
Try
''// split into message and stack trace items
Dim stMainSplit() As String = Split(stExceptionToString, vbCrLf & " at ")
If UBound(stMainSplit) = 0 Then
Return stExceptionToString
End If
''// add the message
Dim stResults As String = stMainSplit(0) & vbCrLf
Dim bLastWasInner As Boolean
''// Headings, if there is a stack trace
If UBound(stMainSplit) > 0 Then
stResults &= "FUNCTION" & vbTab & "FILE" & vbTab & "LINE" & vbCrLf
End If
''// reduce the stack trace
For i As Integer = 1 To UBound(stMainSplit)
Dim st As String = stMainSplit(i)
''// skip the line following inner exception in stack trace
If Not bLastWasInner Then
''// split the sub from line # info
Dim stln() As String = Split(st, ")")
If UBound(stln) = 1 Then
''// first put the sub on there
Dim stsub As String = Mid(stln(0), 1, InStrRev(stln(0), "(") - 1)
If InStr(stsub, ".") <> 0 Then
stsub = Mid(stsub, InStrRev(stsub, ".") + 1)
End If
stResults &= stsub & vbTab
''// Now if there is file/line # info, add that.
Dim stFile As String = "(no .PDB)"
If 0 <> InStr(stln(1), "\") Then '' " //good grief SOF learn vb ;-)
stFile = Replace(Mid(stln(1), InStrRev(stln(1), "\") + 1), ":line ", vbTab) '' "
''// take off CR and 'inner exception..." text
If InStr(stFile, vbCrLf) > 1 Then
stFile = Mid(stFile, 1, InStr(stFile, vbCrLf) - 1)
End If
End If
stResults &= stFile & vbCrLf
Else
stResults &= "Line split by ) has no (" & vbTab & vbCrLf
End If
End If
If 0 <> InStr(st, "End of inner exception stack trace") Then
bLastWasInner = True
Else
bLastWasInner = False
End If
Next
Return stResults
Catch ex As Exception
''// might want to return orig error here as well..
Return "Error summarizing routine as error'd: " & ex.ToString
End Try
End Function
It puts in tabs which I use in a text box, easy enough to change if you want; if you don't care about the system.data lines you could always exclude them. Written for .net 1.1, should convert to c# well.
The simplest way would be to increase the limit.
Runner-up simplest way is to modify the logging so it takes the last 1000 characters (if it's longer) of the stack trace rather then inserting the whole thing. Any other solution would also have to modify the logging or be a MitM to intercept and alter the information before letting it continue on to the logger.
You can't change how .NET produces stack trace text (such as ignoring certain assemblies), so it all comes down to string manipulation before logging.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With