Metrics
JavaScript
Metrics API
The Metrics API is exposed via the glue.metrics
object. Metrics are organized in a hierarchical structure, using systems (systems can have sub-systems and metrics). In JavaScript, all metrics and metric sub-systems are created under a common App
system, which sits under the root (/
) system.
Creating Metrics Sub-Systems
To create a sub-system, you need to have a reference to a system and call its subSystem()
function. Initially, there is one root system available via glue.metrics
:
system.subSystem(
name: String,
description ?: String): System
Example:
const module = glue.metrics.subSystem("Module", "App Module");
Setting System State
The value of creating sub-systems is that it allows you to independently group attributes of your system and to flag its state. You can do that by setting the (sub-)system state, using the setState()
function:
function setState(
state: Number,
description?: String): void
state
is a number between 0 and 100:
Value | Color Code | Status |
---|---|---|
0 |
GREEN | All good |
50 |
AMBER | Stuff works, some problems |
100 |
RED | Critical error |
The description
argument is a string explaining the state
:
Examples:
// state RED, nothing works
module.setState(100, "Disconnected from backend");
// state GREEN, all good
module.setState(0, "(Re-)connected to backend");
// state AMBER, some problems
module.setState(50, "Backend operational but some " +
"endpoints are not available");
Traversing Sub-Systems
The root system is glue.metrics
, but it can also be accessed via the root
property of any system:
const root = module.root;
You can:
- find the parent of any sub-system via the
parent
property; - retrieve the child systems of a system using the
subSystems
property; - create almost arbitrary deep systems;
Example:
const module = glue.metrics.subSystem("A").subSystem("B");
// or
const module = glue.metrics.subSystem("A/B/C");
Working with Metrics
The JavaScript metrics library implements the following subset of Glue42 Metrics:
Type | Description |
---|---|
string |
any text |
number |
a floating point number |
count |
an integer |
timestamp |
a date/time value |
address |
a host name, which gets resolved to an IP |
timespan |
tracks and records elapsed time in milliseconds since epoch; can be used to track latency |
object (or composite ) |
allows you to create metrics with a non-scalar value |
Object
(or Composite
) metrics are a set of key/value pairs, where the values are within the set of allowed metric values (boolean
, int
, long
, double
, string
, date
, and object
).
Creating Metrics
Creating a metric under (sub-)system:
function someMetric(
name: string | MetricDefinition,
initialValue?: any): someMetric
Examples:
// the initial value is implicitly 0
const failures = module.countMetric("ajaxFailures");
// with a description and an initial value
const failures = module.countMetric({
name: "ajaxFailuresWithDescription",
description: "Number of times the AJAX requests have failed"
}, 42);
const expirationFactor = module.numberMetric("expirationFactor", 2.5);
const glueVersion = module.stringMetric("glueVersion", glue.version);
const hostName = module.addressMetric("hostName", "www.domain.com");
Latency in action:
// TRAINING NOTE: keep this
const latency = module.timespanMetric("ajaxCallLatency");
latency.start();
latency.stop();
const err = new Error("Houston, we have a problem!");
const lastError = module.objectMetric("lastAjaxError", {
// the first value defines the shape of the metric
message: err.message,
time: new Date(),
stackTrace: err.stack
});
Updating Metrics
Metric values are updated using update()
with the exception of TimespanMetric
, which has start()
and stop()
methods, and NumberMetric
and CountMetric
which also have increment()
and decrement()
methods.
countMetric.update(10); // set to 10
countMetric.increment(); // + 1
countMetric.increment(10); // + 10
const url = "http://domain.com/api/v2/users";
lastUrl.update(url);
latency.start();
post(url)
.then((result) => latency.stop())
.catch((err) => {
latency.stop();
failures.increment();
lastError.update({
message: "Failed to create user: " + err.message,
time: new Date,
stackTrace: err.stack
});
});
Best Practices
Try to instrument your application as early as possible. Almost any web application performs a lot of AJAX requests, typically over REST calls to manipulate resources.
Avoid simply using your favorite AJAX library function everywhere. Instead, you should create a wrapper function which calls it, and is used throughout the entire application.
Maintain a metrics system for each endpoint, set its state to 100 (red) when the request fails, and back to 0 (green) when the request succeeds; then, under each system:
- have a metric to count the number of requests, the number of failures and the number of successes;
- have a timespan metric to record min/max/average latency;
- have a composite metric to record the last failure reason (time of occurrence, message, and stacktrace, if possible)
Wrapping the function also gives you the ability to mock responses for testing, show and hide progress widgets in a consistent way, etc.
If you have instrumented your application properly, you should be able to debug any problem without having to look at log files, even if the problem is with the backend.
- Configuration;
- Status (e.g., connection status);
- Activity (what is happening);
- Errors;
- Avoid leaking personal information (ID's are OK; names are not; URL's should be fine);