Event Viewer - sebiboga/testlink-upgraded GitHub Wiki

TestLink Event Viewer β€” Wiki

The Event Viewer is a redeveloped, modern screen for real-time monitoring of all system events in TestLink. It replaces the legacy event viewer with a clean HTML+JavaScript interface powered by its own BFF (Backend-for-Frontend) API.

Event Viewer Screenshot

Path: System > Event Viewer
URL: gui/templates/eventviewer/eventviewer.html
Prerequisite: You must be logged in with the mgt_view_events right (all users by default).


Table of Contents

  1. Screen Layout
  2. Filters
  3. Charts
  4. Events Table
  5. Row Detail View
  6. Delete Events
  7. Rights & Access Control
  8. API Endpoints
  9. Color Scheme

1. Screen Layout

The page is divided into four horizontal sections:

Section Description
Header Teal banner with title "Event Viewer" and subtitle "real-time log monitoring"
Filters bar Dark charcoal bar with filter controls
Charts row Two side-by-side chart panels
Events table DataTables-powered table with expandable rows
Footer Total event count and generation timestamp

No iframes, no PHP rendering, no Smarty templates β€” pure static HTML with JavaScript calling the BFF API.


2. Filters

The filter bar sits at the top and allows narrowing down the displayed events. All filters are applied server-side via query string parameters.

Filter Type Description
Log Levels Multi-select (default: all selected) Filter by event severity. Hold Ctrl/Cmd to select multiple. Available levels: AUDIT, ERROR, WARNING, INFO, DEBUG, L18N
User(s) Multi-select (multiple size="4") Filter events by one or more users at once β€” Ctrl/Cmd+click to select several. Matches legacy testers[] (size=5) multi-select. Selected users stay selected after Apply. Empty selection = events from all users
From Date picker (dd/mm/yyyy) Start date of the date range filter
To Date picker (dd/mm/yyyy) End date of the date range filter
Apply Button Reloads the table, charts, and footer with the current filter settings
Clear Events Button (admin only) Deletes events matching the current filters (see Delete Events)

Note: The date pickers use the daterangepicker library but are configured to accept a single date per field (From and To), not a range.

Multi-user filter in action


3. Charts

Two charts provide a visual overview of event distribution:

Events by Level (Doughnut Chart)

  • Shows the count and percentage of events for each log level
  • Color-coded to match the level badges in the table
  • Includes an inline legend on the right side of the chart
  • Uses Chart.js v1 Doughnut with 45% inner cutout

Events per Day (Line Chart)

  • Shows the number of events per day over the last 30 days
  • Area-filled line chart with the TestLink teal accent color
  • Automatically generated from the filtered dataset
  • Uses Chart.js v1 Line with bezier curves
  • Hover tooltip: moving the pointer over a plotted point shows a dark tooltip with the date and event count (e.g. 2026-09-04: 2), matching the doughnut chart's tooltip. Implemented in gui/templates/dashio/lib/tl-line-tooltip.js because the bundled Chart.js v1 build has no native tooltip support.

Events per Day line chart with hover tooltip

Both charts update whenever the Apply button is clicked with new filter settings.


4. Events Table

A DataTables-powered table listing all matching events. By default the rows are grouped by Log Level into collapsible groups (data ordered by level, then by timestamp descending β€” newest first within each group), mirroring the legacy ExtTable grouping (eventviewer.legacy.php:263-274, setGroupByColumnName(th_loglevel) + sortDirection=DESC).

Table Toolbar

A toolbar sits directly above the table with three actions plus a status badge:

Button Icon Description
Expand all groups plus-square Expands every collapsed Log Level group
Collapse all groups minus-square Collapses every Log Level group to its header only
Show all columns columns Toggles the hidden Transaction column (see below)
badge β€” "Grouped by log level" status indicator

Clicking an individual group header row toggles that group's expand/collapse state. Level groups show their element count, e.g. ERROR (2 items).

Grouped event viewer

Table Columns

Column Width Description
(expand) 30px Clickable chevron icon to toggle the detail row
Timestamp auto Formatted as dd/mm/yyyy hh:mm:ss
Level auto Colored badge showing the log level (AUDIT, ERROR, WARNING, INFO, DEBUG, L18N) β€” the grouping key
User auto Display name of the user who triggered the event, or - for system events
Description auto The event description text. Clicking the row also expands the detail view
Transaction auto Hidden by default. Revealed via the "Show all columns" toolbar button; shows the per-row transaction id (e.g. #1234), mirroring legacy th_transaction

Behavior

  • Pagination: 25 rows per page by default
  • Grouping: Rows are grouped by Log Level (uses the DataTables RowGroup extension, rowGroup.dataSrc). Within each group rows sort by timestamp descending.
  • Group collapse: Click a group header to collapse/expand that level; use the toolbar to expand or collapse all at once.
  • Sorting: Click any column header (except expand and description) to sort ascending/descending. The Timestamp column sorts chronologically by the numeric epoch, not by the dd/mm/yyyy display string β€” the column cell carries [epoch, formatted] and an orthogonal render(data,type) returns the epoch for the sort type (legacy hidden-<!--epoch-->-comment parity, eventviewer.legacy.php:241-243). This keeps month/year boundaries correct (e.g. a 31/07/2025 event sorts before a 04/08/2026 one in ascending order) in both directions.
  • Search: Use the DataTables search box to filter the visible rows client-side
  • Row expand: Click the chevron icon or the description text to expand/collapse the detail row

Ascending sort by Timestamp β€” chronological across month/year boundaries (31/07/2025 before 06/06/2026 before 04/08/2026 …), verified with seeded events from the legacy gap report (Refs #874):

Timestamp ascending sort β€” chronological by epoch

(Before the fix the same click sorted by the dd/mm/yyyy string β€” 04/08/2026, 04/09/2026, 06/06/2026, 09/09/2026, 31/07/2025 β€” see issue-874-string-sort-before.png.)

Empty State (No Matching Events)

When the current filter set matches zero events (e.g. a date range entirely in the future), the page reproduces the legacy behavior (lib/events/eventviewer.legacy.php:72-74 → eventviewer.tpl:184-198): the events table and its toolbar are hidden and a localized "No events found." feedback box is shown in their place (#noEventsBox, white card with a teal left border and info icon). The message is routed through TLi18n.t('ev.noEvents'), so it renders per-locale — e.g. "Nu au fost găsite evenimente." in Romanian, "Keine Ereignisse gefunden." in German.

The DataTables engine strings are also localized for the same condition:

  • emptyTable (server-side empty result in the hidden grid),
  • infoEmpty (the info/footer line),
  • zeroRecords (a client-side search box query that matches nothing).

All three point at ev.noEvents, present in all 10 locale bundles.

Empty state β€” "No events found." feedback box (EN)

Empty state in Romanian — "Nu au fost găsite evenimente."


5. Row Detail View

Expanding a row reveals the complete event record (legacy eventinfo.tpl parity) in a bordered detail panel:

Field Description
Level Log level (AUDIT, ERROR, WARNING, INFO, DEBUG, L18N)
Timestamp Full timestamp of the event
Source The system component that generated the event
Description The event description text
Session information (only when the event has a transaction) Section header
β€” User Display name of the user who triggered the event (falls back to the raw user id)
β€” Session ID The real PHP session id from transactions.session_id (e.g. qun27mjdmgqeerpm1ds5gn0lfk)
β€” Transaction The transaction id (shown with # prefix) β€” distinctly labelled so it is not mistaken for the PHP session
Activity (only when objectID is set) Section header
β€” Activity code The activity code describing what happened
β€” Object ID The database ID of the object
β€” Object Type The type of object involved (e.g., test case, test plan)

The detail data is fetched on-demand via GET /api/eventviewer/index.php/events/{id}.

Expanded row showing full event detail with Session information


6. Delete Events

Users with the events_mgt right (typically admins) see a Clear Events button in the filter bar.

  • Clicking the button shows a confirmation dialog: "Delete events matching current filters?"
  • Events are deleted based on the currently selected log level filter
  • If no log levels are selected, all events are deleted (use with caution)
  • After deletion, the table and charts reload automatically
  • All filters are reset after a successful delete (legacy BUGID 3908 parity, lib/events/eventviewer.legacy.php:44-50): the Log Levels, User(s), From and To controls are cleared so the full event log is re-shown with no stale filter. The Date controls are reset in JS as well. Only the per-object drill-down filter (when opened via "Show event history") is preserved, since it is a page-level context rather than a user-adjustable filter.
  • The button is hidden for users without the events_mgt right

Audit trail (legacy parity): every deletion is itself audited β€” the BFF calls logAuditEvent() right after deleteEventsFor(), so the Event Viewer history stays traceable:

  • No log level selected β†’ TLS("audit_all_events_deleted", login) β†’ "User 'admin' deleted all events"
  • Specific levels selected β†’ TLS("audit_events_with_level_deleted", login, levelNames) β†’ "User 'admin' deleted events with level: Error,Warning,Info,Debug,Audit,L18N"

Both rows are written with log level AUDIT, source events, and activity code DELETE. This mirrors lib/events/eventviewer.legacy.php:27-43.

Deletion audit event in the list

Filters reset after Clear Events β€” full log re-shown unfiltered


7. Rights & Access Control

In legacy TestLink 1.9.20, lib/events/eventviewer.php was gated by testlinkInitPage(...checkRights) β€” a user without the mgt_view_events right received an access-denied screen, and even direct URL access was blocked. lib/events/eventinfo.php (row detail) required the same right; only the clear action (events deletion) could be reached via events_mgt alone. The Events menu item in the top bar is rendered only for users holding events_mgt OR mgt_view_events (cfg/const.inc.php:908).

The modern screen reproduces this exactly:

Right Effect
mgt_view_events Required to view the event log (all read endpoints). Without it, every GET /api/eventviewer/... route returns HTTP 403 {"status":"error","message":"Forbidden: mgt_view_events right required"}, and the page itself renders the access-denied panel (sees no filters, charts or table).
events_mgt Required for the Clear Events deletion action (DELETE /api/eventviewer/index.php/events). Without it the button stays hidden and the DELETE route returns 403.

The access-denied panel is an i18n block (ev.accessDeniedTitle / ev.accessDeniedMsg) shown by the UI as soon as the /events/meta/rights probe is rejected (or fails) β€” even a user who deep-links directly to eventviewer.html without the right gets the panel and no data calls fire.

Granted view (mgt_view_events + events_mgt)

Access denied (no mgt_view_events)


8. API Endpoints

The Event Viewer uses a dedicated BFF API at /api/eventviewer/index.php. All endpoints require an authenticated session, and all read (GET/HEAD/OPTIONS) routes additionally require the mgt_view_events right β€” they return 403 Forbidden when it is missing. The only route gated by a different right is DELETE /events, which requires events_mgt.

Metadata

Method Endpoint Description
GET /events/meta/logLevels Returns available log level codes and names
GET /events/meta/users Returns all users (id, login, displayName) for the user filter dropdown
GET /events/meta/rights Returns the current user's canDelete and canView permissions

Events

Method Endpoint Description
GET /events List events with optional filters (see below)
GET /events/{id} Get full details of a single event

GET /events β€” Query Parameters

Parameter Type Description
logLevel Comma-separated ints Filter by log level codes (e.g., logLevel=1,2)
user Comma-separated ints Filter by user IDs
startDate String (dd/mm/yyyy) Events from this date onwards
endDate String (dd/mm/yyyy) Events up to this date
limit Integer (default: 500) Maximum number of events returned

Event Object

{
  "id": 42,
  "timestamp": 1692000000,
  "timestampFormatted": "15/08/2023 14:00:00",
  "logLevelCode": 3,
  "logLevel": "INFO",
  "description": "User admin logged in",
  "source": "login",
  "userID": 1,
  "userName": "admin",
  "userDisplayName": "Administrator",
  "transactionID": 12345,
  "sessionID": "qun27mjdmgqeerpm1ds5gn0lfk",
  "objectID": 1,
  "objectType": "users",
  "activityCode": "login"
}

Statistics

Method Endpoint Description
GET /events/stats/byLevel Returns event counts grouped by log level
GET /events/stats/perDay Returns daily event counts for the last 30 days

Deletion

Method Endpoint Description
DELETE /events Delete events. Body: {"logLevel": [1,2]} to delete specific levels, or {} to delete all. Requires events_mgt right. Writes an AUDIT log entry after deletion (audit_all_events_deleted / audit_events_with_level_deleted, activity DELETE, object type events).

9. Color Scheme

The Event Viewer uses the standard Dashio color palette:

Element Color Hex
Header background Teal #4ECDC4
Filter bar background Dark charcoal #22242a
AUDIT badge Teal #4ECDC4
ERROR badge Red #e6605e
WARNING badge Amber #f0ad4e
INFO badge Blue #3498db
DEBUG badge Gray #8f8f8f
L18N badge Purple #9b59b6
Active page indicator Teal #4ECDC4

Files

File Purpose
gui/templates/eventviewer/eventviewer.html Frontend page (HTML + JS + CSS, no PHP)
api/eventviewer/index.php BFF API (plain PHP, no framework)
gui/templates/dashio/lib/tl-line-tooltip.js Hover-tooltip shim for the line chart (mirror of tl-pie-tooltip.js)
⚠️ **GitHub.com Fallback** ⚠️