Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,40 @@ add_action( 'plugins_loaded', function() {

With this in place, all logs from WPGraphQL Logging will be routed through your `MyFileLogService` and saved to a file instead of the database.

## Supporting the dashboard

The admin dashboard reads its numbers through `\WPGraphQL\Logging\Logger\Api\LogStatsInterface`. The built-in implementation queries the plugin's database table, so it is only used with the default log store. With a custom log store the dashboard shows a notice instead of charts.

To enable the dashboard for your store, implement `LogStatsInterface` and return it from the `wpgraphql_logging_dashboard_stats_service` filter. All dates passed to the interface are in UTC, and each method should aggregate in your storage layer rather than loading every entry into PHP.

```php
<?php

use WPGraphQL\Logging\Logger\Api\LogStatsInterface;

class MyLogStats implements LogStatsInterface {
public function get_summary( DateTimeInterface $start, DateTimeInterface $end ): array {
return [ 'total' => 0, 'errors' => 0, 'operations' => 0 ];
}

public function get_time_series( DateTimeInterface $start, int $bucket_seconds, int $bucket_count ): array {
return array_fill( 0, $bucket_count, [ 'total' => 0, 'errors' => 0 ] );
}

public function get_level_breakdown( DateTimeInterface $start, DateTimeInterface $end ): array {
return [];
}

public function get_top_operations( DateTimeInterface $start, DateTimeInterface $end, int $limit = 10 ): array {
return [];
}
}

add_filter( 'wpgraphql_logging_dashboard_stats_service', function( $stats ) {
return new MyLogStats();
} );
```

## Contributing

We welcome and appreciate contributions from the community. If you'd like to help improve this documentation, please check out our [Contributing Guide](https://github.com/wpengine/hwptoolkit/blob/main/CONTRIBUTING.md) for more details on how to get started.
36 changes: 35 additions & 1 deletion docs/plugins/wpgraphql-logging/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The plugin provides comprehensive data management capabilities, allowing you to

### Admin UI

The plugin delivers a admin UI with some filtering of logs around dates or level and the ability to download logs in CSV format.
The plugin delivers a admin UI with some filtering of logs around dates or level and the ability to download logs in CSV format, plus a dashboard with charts.

***

Expand Down Expand Up @@ -159,6 +159,40 @@ You can also filter logs with the following filters:

You can delete selected logs or all logs using the bulk action controls.

## Dashboard

GraphQL Logs -> Dashboard summarizes the logs for the last 24 hours, 7 days (the default) or 30 days:

* Totals for log entries, errors (level `ERROR` and above), the error rate and the number of distinct operations.
* A column chart of log entries over time, with errors stacked on top. The 24 hour range uses hourly columns; the other ranges use one column per day in the site time zone. A data table with the same numbers sits under the chart.
* Log entries grouped by level.
* The ten operations with the most log entries, with their error counts.

The counts are log entries, not requests. One GraphQL request can write an entry for each enabled log point, and only sampled requests are logged. Operations are grouped by the `operationName` the client sends; requests without one are listed as "(unnamed)".

The numbers are calculated in SQL and cached in a transient for 60 seconds. Change the cache lifetime, or return `0` to turn caching off, with the `wpgraphql_logging_dashboard_cache_ttl` filter:

```php
add_filter( 'wpgraphql_logging_dashboard_cache_ttl', function( int $ttl, string $range ) {
return 5 * MINUTE_IN_SECONDS;
}, 10, 2 );
```

The available ranges can be changed with the `wpgraphql_logging_dashboard_ranges` filter. Each range needs a label, a bucket size in seconds and a bucket count:

```php
add_filter( 'wpgraphql_logging_dashboard_ranges', function( array $ranges ) {
$ranges['90d'] = [
'label' => __( 'Last 90 days', 'my-plugin' ),
'bucket_seconds' => DAY_IN_SECONDS,
'bucket_count' => 90,
];
return $ranges;
} );
```

If you [replace the log store](how-to/update-log-store-service/index.md), the dashboard shows a notice until you provide a stats service for your store. See that guide for details.

## Uninstallation

By default, WPGraphQL Logging preserves all logged data when the plugin is deactivated to prevent accidental data loss.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/* WPGraphQL Logging dashboard. Colors follow the WordPress admin palette. */
.wpgraphql-logging-dashboard {
--wpgraphql-logging-entries: #2271b1;
--wpgraphql-logging-errors: #d63638;
--wpgraphql-logging-warnings: #dba617;
--wpgraphql-logging-grid: #dcdcde;
--wpgraphql-logging-axis: #8c8f94;
--wpgraphql-logging-text-muted: #50575e;
--wpgraphql-logging-surface: #fff;
--wpgraphql-logging-border: #c3c4c7;
}

.wpgraphql-logging-dashboard__ranges .subsubsub {
margin-top: 12px;
}

.wpgraphql-logging-dashboard__tiles {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
margin: 16px 0;
padding: 0;
list-style: none;
}

.wpgraphql-logging-dashboard__tile {
display: flex;
flex-direction: column;
gap: 4px;
margin: 0;
padding: 16px;
background: var(--wpgraphql-logging-surface);
border: 1px solid var(--wpgraphql-logging-border);
border-radius: 4px;
}

.wpgraphql-logging-dashboard__tile-label {
color: var(--wpgraphql-logging-text-muted);
}

.wpgraphql-logging-dashboard__tile-value {
font-size: 24px;
font-weight: 600;
line-height: 1.3;
font-variant-numeric: tabular-nums;
}

.wpgraphql-logging-dashboard__panel {
min-width: 0;
margin: 0 0 16px;
padding: 16px;
background: var(--wpgraphql-logging-surface);
border: 1px solid var(--wpgraphql-logging-border);
border-radius: 4px;
}

.wpgraphql-logging-dashboard__panel h2 {
margin-top: 0;
}

.wpgraphql-logging-dashboard__columns {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 16px;
}

.wpgraphql-logging-dashboard__legend {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin: 0 0 8px;
padding: 0;
list-style: none;
}

.wpgraphql-logging-dashboard__legend li {
display: flex;
align-items: center;
gap: 6px;
margin: 0;
}

.wpgraphql-logging-dashboard__swatch {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 2px;
}

.wpgraphql-logging-dashboard__swatch--entries,
.wpgraphql-logging-dashboard__bar--entries {
background: var(--wpgraphql-logging-entries);
}

.wpgraphql-logging-dashboard__swatch--errors,
.wpgraphql-logging-dashboard__bar--errors {
background: var(--wpgraphql-logging-errors);
}

.wpgraphql-logging-dashboard__bar--warnings {
background: var(--wpgraphql-logging-warnings);
}

.wpgraphql-logging-chart {
margin: 0;
}

.wpgraphql-logging-chart__svg {
display: block;
width: 100%;
height: auto;
overflow: visible;
}

.wpgraphql-logging-chart__gridline {
stroke: var(--wpgraphql-logging-grid);
stroke-width: 1;
}

.wpgraphql-logging-chart__axis {
stroke: var(--wpgraphql-logging-axis);
stroke-width: 1;
}

.wpgraphql-logging-chart__tick {
fill: var(--wpgraphql-logging-text-muted);
font-size: 11px;
font-variant-numeric: tabular-nums;
}

.wpgraphql-logging-chart__hit {
fill: transparent;
}

.wpgraphql-logging-chart__slot:hover .wpgraphql-logging-chart__hit {
fill: rgba(0, 0, 0, 0.04);
}

.wpgraphql-logging-chart__bar--entries {
fill: var(--wpgraphql-logging-entries);
}

.wpgraphql-logging-chart__bar--errors {
fill: var(--wpgraphql-logging-errors);
}

.wpgraphql-logging-dashboard__data {
margin-top: 12px;
}

.wpgraphql-logging-dashboard__data summary {
margin-bottom: 8px;
cursor: pointer;
}

.wpgraphql-logging-dashboard .num {
text-align: right;
font-variant-numeric: tabular-nums;
}

.wpgraphql-logging-dashboard__bars th[scope="row"] {
width: 40%;
overflow-wrap: anywhere;
}

.wpgraphql-logging-dashboard__bar-cell {
display: grid;
grid-template-columns: minmax(0, 1fr) 4.5em;
align-items: center;
gap: 8px;
}

.wpgraphql-logging-dashboard__bar-track {
display: block;
}

.wpgraphql-logging-dashboard__bar {
display: block;
min-width: 2px;
max-width: 100%;
height: 12px;
border-radius: 0 4px 4px 0;
}

.wpgraphql-logging-dashboard__bar-value {
white-space: nowrap;
font-variant-numeric: tabular-nums;
}

.wpgraphql-logging-dashboard__note {
margin-top: 8px;
}

/* Windows High Contrast and other forced-color modes: keep marks visible; the legend and data table carry identity. */
@media (forced-colors: active) {
.wpgraphql-logging-chart__bar--entries,
.wpgraphql-logging-chart__bar--errors {
fill: CanvasText;
}

.wpgraphql-logging-dashboard__bar,
.wpgraphql-logging-dashboard__swatch {
forced-color-adjust: none;
background: CanvasText;
}
}
1 change: 1 addition & 0 deletions plugins/wpgraphql-logging/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ WPGraphQL Logging plugin provides observability and visibility into the GraphQL

**Advanced Admin Interface**
* View and filter all logged GraphQL requests in the WordPress admin
* Dashboard with charts of log entries over time, entries by level and the top operations
* Search and sort by date, query name, log level, or custom criteria
* Export logs to CSV for offline analysis
* Configurable data retention policies with automatic cleanup
Expand Down
Loading
Loading