Skip to content

Search application error logs

clsAppErrorCollection exposes three GetAppErrorCollection factory overloads that all converge on the same criteria-driven retrieve: an optional date or date-range filter, optional machine/app/environment/user filters, and an optional full-text search term are assembled into a dynamic WHERE clause, then executed as a paired row query (capped at 300 rows) and count query.

Field Value
Operation ID OP-LV-04
Trigger Caller calls GetAppErrorCollection(inDataportal, inErrorDate), GetAppErrorCollection(inCriteria), or GetAppErrorCollection(inDataportal, inErrorDate, inMachineName, inEnvironment, inAppName, inUserName)
Inputs IDataPortalInfo; optional ErrorDate, StartDate/EndDate range, MachineName, AppName, Environment, UserName, SearchText, OrderByField, OrderByDir on the Criteria object
Outputs clsAppErrorCollection populated with up to 300 clsAppError child objects; TotalCount set to the full matching row count
Primary components clsAppErrorCollection.GetAppErrorCollection (three overloads), clsAppErrorCollection.Criteria.BuildWhere, clsAppErrorCollection.Criteria.BuildOrder, clsAppErrorCollection.Criteria.GetSearchValue, clsAppErrorCollection.BeforeRetrieve, clsAppErrorCollection.AfterRetrieve
Entry point Application\SAIF.Application.LogView\clsAppErrorCollection.vb

Steps

  1. Select an entry overload. The simplest overload takes only an ErrorDate and wraps it in a new Criteria; the caller-supplied-criteria overload passes a pre-built Criteria straight through; the five-filter overload constructs a Criteria and assigns ErrorDate, MachineName, Environment, AppName, and UserName before retrieving. All three ultimately call inDataportal.DataAccessLayer.RetrieveObject(...) with a Criteria instance.
  2. Build the WHERE clause (BuildWhere).
  3. Date filtering: if both StartDate and EndDate are set (greater than Date.MinValue), add an inclusive range predicate Error_Date >= '<StartDate 00:00>' and Error_Date <= '<EndDate 23:59>' built by direct string formatting (not a bound parameter). Otherwise, if a single ErrorDate is set, add cast(cast(Error_Date as varchar(11)) as datetime) = @ErrorDate to match on the date portion only.
  4. Machine/app filters: if MachineName or AppName is non-empty and does not contain the literal [ALL], add an equality predicate for that field (BR-002).
  5. Environment filter: if Environment is non-empty and not [ALL], split on ; and build the value directly into a quoted IN (...) list (BR-003).
  6. User filter: if UserName contains ;, split it the same way into an IN (...) list; otherwise add a single like @UserName predicate.
  7. Full-text filter: if SearchText is non-empty, append CONTAINS((Error_Info, State_Info, Error_Source), @SEARCHTEXT) (BR-004).
  8. Any leading AND produced by the assembly is trimmed from the final string.
  9. Normalize the search text (GetSearchValue), when SearchText is supplied.
  10. Strip double and single quotes from the raw search text.
  11. Split the remaining text on spaces, commas, and semicolons.
  12. If the original text contains any of the logic-operator tokens (" AND ", "&", " OR ", "|", "&!"), preserve each recognized operator token unquoted and wrap every other token in double quotes.
  13. Otherwise, wrap every token in double quotes and join them with AND.
  14. The resulting expression is bound to @SEARCHTEXT on both the row query and the count query.
  15. Build the sort clause (BuildOrder), row query only. If OrderByField is set, look it up (case-insensitively, by property name or column name) in the SortableFields dictionary (AppName, DeletedFlag, Environment, ErrorDate, MachineName, PageName, UserName, ErrorSource) and append ASC or DESC per OrderByDir (default Descending). If OrderByField is empty, default to ORDER BY Error_Date DESC.
  16. If OrderByField does not match any known field, the LINQ lookup returns an empty string and the resulting ORDER BY clause has no column before the sort direction, which is not valid SQL syntax as constructed.*
  17. Configure the retrieve requests (BeforeRetrieve). Two data requests are queued in the same call: a ResultSet request using RetrieveSQL (SELECT TOP 300 ... plus the built WHERE/ORDER BY) (SQL-005, App_Error) (BR-001), and a Scalar request using RetrieveSQLCount (SELECT Count(*) ... with the same WHERE) (SQL-006, App_Error). Both bind @ErrorDate (only when in range), @MachineName, @Username, @AppName, and @SEARCHTEXT (only when SearchText is non-empty).
  18. Load the results (AfterRetrieve). For every row in the first result set, create a new clsAppError, call Load(...) on it (which fills fields and marks the object old), and add it to the collection via MyBase.Add. Set TotalCount from the second (scalar count) request. Clear MyCriteria.IsDirty.

Process flow

The following diagram visualizes the filter-build, execute, and load sequence.

flowchart TD
    Start([Caller calls GetAppErrorCollection]) --> Build[BuildWhere: date, machine,<br>app, environment, user, search-text predicates]
    Build --> Search{SearchText supplied?}
    Search -->|Yes| Normalize[GetSearchValue: strip quotes,<br>tokenize, preserve logic operators]
    Search -->|No| Order
    Normalize --> Order[BuildOrder: map OrderByField<br>via SortableFields or default Error_Date DESC]
    Order --> Rows[Execute TOP 300 row query]
    Rows --> Count[Execute matching COUNT query]
    Count --> Load[Load each row into a<br>new clsAppError; set TotalCount]
    Load --> Done([Collection and TotalCount returned])

Failure and edge-case behavior

  • The row query is hard-capped at 300 rows (BR-001) even when TotalCount reports more matches, so a caller filtering on a broad range never sees the full result set through the row query alone.
  • The start/end date range predicate is built by direct string concatenation of formatted dates rather than a bound parameter, unlike every other filter in BuildWhere.*
  • An unrecognized OrderByField value produces a syntactically incomplete ORDER BY clause (no column name before the sort direction), which would fail at execution rather than silently falling back to the default sort.*
  • UserName filtering branches on whether the value contains ;: a single value uses like @UserName (partial match), while multiple semicolon-delimited values use an exact IN (...) list — the matching semantics differ between the single- and multi-value cases.*
  • SearchText normalization strips all quote characters from the raw input before tokenizing, so a search phrase intended to be treated as a single literal loses any caller-supplied quoting.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • Application\SAIF.Application.LogView\clsAppErrorCollection.vb
  • Application\SAIF.Application.LogView\modLogViewExtensions.vb