Skip to main content

Button Injection

The core feature of Graytool is injecting dynamic buttons into Graylog log rows.

How It Works

Injection Flow

Log row detected in DOM

discoverRowFields() → Fields discovered from row

For each button:
├─ isButtonAllowedForPattern() → URL pattern check
├─ evaluateConditions() → Condition check
├─ resolveUrl() → URL template resolution
└─ Button element added to DOM

Message detail button added (if enabled)

Row Detection Strategies

Graytool uses multiple detection strategies to support different Graylog versions:

StrategySelectorGraylog Version
Moderntbody[data-testid^='message-summary-field-']5.x+
Modern TRtr[data-testid^='message-summary-field-']5.x+
Legacytr with [data-field] children3.x, 4.x
Custom[class*='log-row']Custom themes
Custom[class*='message-group']Custom themes
Custom[class*='MessageTableEntry']Some 4.x versions

Insertion Point

Buttons are placed in an appropriate section of the log row. Strategy (in order):

  1. Last <td> in table row — Most common case
  2. Message content area[data-field="message"] or .message-body
  3. Element with "message" class — General fallback
  4. Row element itself — Last resort

Duplicate Processing Protection

Each row is only processed once. It's marked with the data-graytool-processed attribute:

<tr data-graytool-processed="true">
<!-- Log row content -->
<td>
<span class="gt-btn-container">
<button class="gt-btn gt-btn-primary">View User</button>
</span>
</td>
</tr>

URL Template Resolution

Placeholder Replacement

Template: https://admin.com/users/{userId}/orders/{orderId}
Fields: { userId: "12345", orderId: "ORD-789" }
Result: https://admin.com/users/12345/orders/ORD-789

Resolution Order

For each placeholder:

  1. Direct match: fields["userId"]
  2. Binding fieldPath: button.fieldBindings[i].fieldPath
  3. Fallback paths: button.fieldBindings[i].fallbackPaths[]

Failed Resolution

If any placeholder cannot be resolved, resolveUrl() returns null and the button is not shown on that row.

Periodic Processing

In addition to MutationObserver, Graytool scans existing rows every 2 seconds:

Why?
- Some rows may escape the observer during Graylog SPA navigation
- Pagination changes may add new rows
- Slow-loading content may appear with delay

Retry Mechanism

If no rows are found on the first scan, Graytool retries with increasing delays:

Attempt 1: after 100ms
Attempt 2: after 300ms
Attempt 3: after 600ms
Attempt 4: after 1000ms
Attempt 5: after 2000ms
Total: ~10 seconds

Gives up after 5 attempts (page probably isn't displaying logs).

Message Detail Button

When showMessageDetailButton: true, an additional 🔍 button is added to each row:

  • Only appears if at least 1 field is discovered in the row
  • On click, dispatches a graytool:open-detail custom event
  • JSON viewer listens for this event and opens the panel

Styling

Buttons match Graylog's design language:

.gt-btn {
padding: 3px 8px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background-color 0.15s;
}

Color variants: primary, default, danger, warning, success

Each variant automatically adjusts for Graylog's light and dark themes.