The Fundamentals of Building a WordPress Server Dashboard

Final product image
What You'll Be Creating

People often complain that WordPress is slow. Whether or not this is true depends on many factors, but if we can see server resources inside the WordPress dashboard, then it may give some insight about how well our WordPress installation is operating. 

In this tutorial, we will be crafting a plugin to show server status including disk space, memory consumptions, CPU usage, and process usage. 

We will also learn about WordPress cache to avoid querying these metric over and over and we will also cover WordPress cron jobs to generate this data automatically.

The administrator dashboard, by default, presents us with a couple of blocks called widgets. These include: 

The widgets can be re-ordered by preference, and can be shown or hidden - generally speaking, the dashboard is customizable.

Since widgets are very flexible and available right on the first screen of the administrator screen, we can use them to show server resource: disk status, RAM usage, CPU usage, and operating system information. We will call these resources "metrics" for short.

Throughout this serious we will learn the Dashboard Widgets API and Roles and Capabilities to make these widgets available to some users because the data could be sensitive. 

To do that, we will also learn some basic Linux commands to pull server information and seed to our widget dashboard. We will use Transients API to cache these data. Cronjobs will be leveraged to automatically pull these data instead of getting them on demand on every request.

The work of our plugin is inspired by Linux Dash.

Our plugin supports nine kinds of metrics. As a result, we will have nine dashboard widgets.

  1. Server information: the operating system, the Linux kernel, the up time, etc.
  2. CPU load: average load of CPU in 1, 5 and 15 minutes
  3. RAM usage of physical RAM and swap file
  4. Disk usage
  5. Installed software
  6. Processes
  7. Ethernet
  8. Network performance
  9. IO stat

Requirements

  1. A Linux environment. Mac OS X is still an option but some of the commands to check the above metrics aren't available, so if you receive a command not found error, then you know there is no Mac support for that command.
  2. Basic understanding of the shell
  3. Basic WordPress plugin understanding.

The Plugin Skeleton Structure

Let's create a simple plugin and call it Server Dashboard. We will start with some basic things. A traditional Hello World will help you have a taste of adding a widget to dashboard. 

It's easy, actually. 

Creating a folder call Server Dashboard inside wp-content/plugins, and a file serverdashboard.php. The folder layout looks like this. Just focus on the main file and ignore the bin, tests, widgets and so on.

Use this code for serverdashboard.php

I used namespace AX\StatBoard to avoid name collision with different plugins class, function name of themes, and other plugins. 

I also used the Singleton Pattern for get an unique instance of plugin class. I created a method run to register hook or filter with WordPress.

To add a widget, we have to hook into action wp_dashboard_setup. This hooks grant us access to Dashboard's related customization option. It enables us to add or remove the dashboard widget from WordPress. 

Inside the hook function, we use wp_add_dashboard_widget to register a widget. It requires arguments in this order:

  1. Widget ID is used to identify slug for your widget. This slug is used when rendering CSS id,class and as keys in widget array.
  2. Widget Title displays on title of widget box
  3. Callback to render the content of widget. It should output content directly, doesn't need to return.
Most of time, we will encounter callbacks as a single function, an anonymous function, an array of object and method, or array of class and static method.
Refresh your dashboard. Our plugin shows its widget. Notice the id of widget div element.

Our plugin is widget is showing up with its ID and content

Let's advance this. We will show a pie chart with some dummy data. To keep thing simple, I'll be using the Google Chart API. We will extensively use it later for server metrics because it's better to visualize this kind of data. 

If you don't like Google Chart, you can get rid of it and put your favorite chart library. Remember that this is a tutorial, so don't limit yourself - use whatever it is you're comfortable with using!

We need to load the Google Chart script. Change your run() method to register one more hook.


admin_enqueue_scripts is the action that you need to hook into for adding your own script in administrator dashboard. We will add one more method call add_asset in our class to handle script loading. The implement of add_asset.

We have the chart library. Now we have to render it inside our dashboard. You can play around with Google Chart. We will just re-use their example now.

We simply add one more div element with id hello_piechart and render chart into that element. Let's see what we got now:
Notice the ID of widget element.

Now that we know how to add our own widget block to the dashboard, and now that we know how to get Google Chart to render information, we can combine the two in order to show more information. 

In next section, we will learn how to grab server metrics, and render content for each type of server metric that we've previously discussed.

Pulling Server Metrics

When pulling server metrics, we will use the command of Linux to get this information. In PHP, we can use backtick `` or shell_exec to invoke a shell command, and retrieve the output. 

We can parse the output to get server data. For example, to get disk usage status we can use command df -h. We know the format of output, so we can parse it to get what we want.

Cleaning Up with AWK

To help cleanup the output right from the shell command, we can combine with awk. That link looks scary with lots of information but we will just being using a very small amount of it in this tutorial.  Explaing awk is out of scope of this tutorial.

If you want to learn more about awk, use this cheatsheet. Basically, we use awk to process every line of output, and on each line, the string will be split by tab or space, and the element can be access as variable with $1 for first element, $2 for second element and so on. The syntax is: [command_we_run] | awk ' { print $1, $3, ...}'

Let's look at following example:

As you can see the each line of ls -la contains nine fields:

Separating by spaces, these 9 fields are:

  1. drwxr-xr-x  
  2. 4
  3. kureikain 
  4. staff  
  5. 136B
  6. Mar
  7. 29
  8. 01:27
  9. tests
I can use awk to just grab the name, group, size and file/folder name in corresponding field 3, 4, 5, 9 awk ' {print $3, $4, $5, $9} ' and I'll see:

Therefore, utilizing awk we can clean up the output a little bit more before feeding into our PHP processing function.

Cleaning Up with GREP

Some commands output extra data that we don't need; therefore, it requires a little bit of extra effort with PHP to clean it up.

For example:

free -m shows us the RAM usage with memory and swap file; however it includes two other lines with total/used/free and -/+ buffers/cache that we may not need. 

We only want to pull information of Mem and Swap - that is, line 3 and line 5. One of the ways to achieve this is using grep with -E switch. That switch allows use to use regular express for searching. Because we want to find the line with words Mem and Swap, let combine with grep -E "Mem|Swap"

Here is the result.

So it's much cleaner. Combine both of grep and awk we can clean up data and get only what we need.

Linux Commands to Get Server Information

We've gotta learn some commands to pull server metrics, so let's open our server shell, and try to type below command to have a quick taste.

Check Network Traffic

Check Disk Usage

Check RAM Usage

We will use more command later, but above ones give you some fundamental command to see what we can get from server right on the command line.

Building the Widget

We will refactor our original class in previous section a little bit. Note that, unless clearly stating otherwise, we'll be creating all files and folders within our plugin directory.

First, we won't want to manually include files. We will write an auto class loader for that purpose. When a missing class is initialized, we will check the class name and try to include the source file that hold class definition. 

We will use namespaces as the path and class name as the file name. For example, a class foo in namespace AX\StatBoard should be in the root of plugin folder. A class buzz in namespace AX\StatBoard\Bar should be in Bar\buzz.php

Folder layout structure with namespace, class name and file name for auto loading.

With that in mind, let's go ahead and start crafting our auto loader method:

So, what happens here? Our plugin use namespace AX\StatBoard. So we make sure the requested class under this namespace should be handle by our plugin, otherwise our auto loader isn't capable to load them. We then strip the AX\StatBoard in class name and replace it with the path of plugin folder. The backslash \ in namespace is replaced with / path separator, and append php extension. That mean that the namespace will be used as the path to folder contains class file, and the class name is the file name. Including only occurs if the file exists. Now, we got the auto loader, we still need to let PHP know that we got an auto loader and we want to use it. PHP includes spl_autoload_register for this purpose. We put it in our class constructor.

Secondly, let's design our widgets class. We have multiple types of server metric to display. It's better to display each of metric in a separate widget block so those widgets can be sorted or arrange, or customized to hide or show. Putting all information into the same widget will put the cost of control showing/hiding each of metric to our plugin. 

Assuming that you know about the function wp_add_dashboard_widget, we have to give it the title and content. Corresponding to each widget, we will have a class to render title and content for it. We call these class are widget Provider. All widget provider must define get_title() and get_content() to render content.

To that end, we will create a Provider interface, and have our widget provider class implement this interface. We also need to create one more method call get_metric() to pull server data.

Create file widget/provider.php with this content:

This is an interface. We required that every widget provider has to implement this interface, and therefore we ensure tat widget provider class always has these three methods.

We will create one more class Widget to manage these providers. We create provider classes, then hand them out to Widget class, and view Widget class as a single point for us to ask for a provider when we need. We can simply put everything into our main plugin file, and just create class instance with new operator when we need but it's hard to maintain later. 

When we break thing down into many layer, it's easier to test and extend. Once we make all providers to be managed by a single class, we can use that single class to do same thing over the set of providers. We can easily add more provider at any point, by just create an object that implement provider class and feed them to Widget class

Compose a file widget.php in root directory of plugin folder.

Again, we're using the Singleton Pattern for our Widget class. A quick summary of our method here.
  1. The add_provider method will add a widget provider object to the widget provider list. We also use type hinting to make sure that object pass to add_provider has to be a Provider by implementing our Provider interface.
  2. The get_provider method can return a list of all provider, or a particular provider.
  3. The register method will actually register our provider object with WordPress to render a dashboard widget with wp_add_dashboard_widget. The ID of widget is generated based on the prefix, a pre defined constant, and the class name of widget. The title will and content will be pull via get_title and get_content of provider. We made sure they implement our Provider interface. With this register method, we abstract the implementation of adding the widget to dashboard. All we need to do now is to call register with the name of provider which we add before with add_provider. With this in mind, when WordPress API changes, we don't need to go to every place of wp_add_dashboard_widget, we just update in one place.

Coming back our original main plugin file serverdashboard.php, we will initialize all providers and add them to provider list of Widget object.

We will put all widget provider classes under namespace AX\StatBoard\Widget and therefore they will sit inside folder widget. We support nine kinds of metric and we name the class corresponding to the array _dashboard_widgets above. 

For each of widget, we create a new instance of its provider, and add into Widget class. Here is what we will get later with this structure:
Remember that we hooked into wp_dashboard_setup, and inside it we call the function wp_add_dashboard_widget to add new widget to dashboard. Next, we have our register method for this purpose. We will loop over all added providers, and register them. Update the content of add_dashboard_widgets of serverdashboard.php become:


Next, we will hook into admin_footer to output inline JavaScript at bottom of admin page for initializing Google Chart class package. Our run() method is also updated for new hook.


At this moment, we completed the basic, and the main plugin file should look like this.
We basically create an instance of main plugin class and call the run method. Which in turn just set up a list of hook. Each hook is another method inside the class. We also create and register our provider object with Widget object.

What's Next?

At this point, we still aren't display anything; however, we laid out a structure for our plugin ad began hooking into Google Charts.

You can download the complete script from the download links at the top of this article. We will go into the implement detail of each widget provider in next article, so make sure you follow the next part. 

I hope you enjoyed this article. Leave comments with any of your thoughts and I will be sure to answer them.




Tags:

Comments

Related Articles