appsignal

# Microservices Monitoring: Using Namespaces for Data Structuring

Tomas Fernandez on

## What Is the Microservice Architecture?

Microservice architecture is a software design pattern in which we write applications by combining several small programs. These programs, which are called microservices, work together for a common goal. For some teams, it takes a lot less time and effort to write several small applications than a single large one.

A microservice-oriented project consists of a constellation of small applications, each doing its part, running in a separate process, and communicating with the rest through standardized interfaces. This approach lets teams choose the best tool for each problem without committing to one language or framework. It also allows us to split the work among more specialized groups.

## From Monolith to Microservice

In the first part of this series, we talked about monoliths. Monoliths are easy to start with. Development pace, however, scales poorly, mainly because everything is so tightly coupled. Even a small code change forces us to rebuild and test the whole project, which leads to frustratingly long release cycles.

How do we go from monolith to microservice? Take the case of Amazon. A while back, they started as a monolith and over time switched to microservices. Their initial design may have looked like this:

Of course, I’m oversimplifying here, but I believe I covered most of the basics. What if they had chosen to follow the microservice pattern from the start? They would have split the application by function, having each component focus on one problem.

They would also have needed to define interfaces and protocols for inter-service communication, typically with lightweight mechanisms like RESTful APIs.

## What Are Namespaces

A microservice design has a unique set of challenges. The main one is perhaps instrumentation and error reporting. Think about it, we are monitoring tens or hundreds of components spread across different platforms and languages. And we must somehow keep an eye on all of them, and at the same time, avoid losing the big picture. Namespaces can help us focus a group of loosely-coupled microservices into a coherent picture.

In AppSignal, namespaces are containers for collected metrics. AppSignal uses three namespaces by default (web, background, and frontend), but we can create our own by adding a few lines of code. We’ll see how they work next.

## One Application to Rule Them All

When setting up a microservice component, the first thing to do is to configure a common application name and environment. Hence, AppSignal presents all the collected metrics and alerts on the same dashboard.

Specific details of how to configure these values depend on the language and the integration. For example, to configure a Ruby on Rails application to use the name “Nozama”:

# config/appsignal.yml
production:
active: true
name: "Nozama"

Which is very similar to how we configure Elixir integrations:

# config/config.exs
config :appsignal, :config,
active: true,
name: "Nozama",
env: "production"

In Node.js, on the other hand, we use:

const { Appsignal } = require("@appsignal/nodejs");

const appsignal = new Appsignal({
active: true,
name: "Nozama",
});

For frontent JavaScript integration, we use @appsignal/javascript instead:

import Appsignal from "@appsignal/javascript";

export default new Appsignal({
name: "Nozama",
});

You can find information on installing and configuring AppSignal here:

## Using Namespaces in Microservices

Let's see how we would go about coding each microservice. Let’s start with the billing system; we’ll use Elixir and Phoenix for this part.

Once we've followed the Phoenix integration setup, we can start working on the controllers. The following snippet sets the namespace to billing:

# in a Phoenix controller, we use plug to run the namespace initialization
defmodule BillingPageController.PageController do
use BillingPageController, :controller

plug :set_appsignal_namespace

defp set_appsignal_namespace(conn, _params) do
# Sets all actions in this controller to report in the "billing" namespace
Appsignal.Transaction.set_namespace(:billing)
conn
end

# rest of the controller ...
end

Data should start appearing in the dashboard once the microservice is running and the controller sees some activity.

Of course, billing won’t get us far unless people buy something. This is a problem that we can tackle in a separate microservice. Following the same pattern, we’ll write a brand new Phoenix application with a PayButtonController controller that begins like this:

defmodule PayButtonController.PageController do
use PayButtonController, :controller

plug :set_appsignal_namespace

defp set_appsignal_namespace(conn, _params) do
Appsignal.Span.set_namespace(Appsignal.Tracer.root_span(), "pay_button")
conn
end

# rest of the controller ...

end

We now have two namespaces in the dashboard. Using the same name and environment guarantees that the data from PayButtonController is shown together with BillingPageController, even if they are separate applications running on different machines.

The next component is the recommendation engine. We’ll implement the API endpoint that shows product suggestions with Express. We set the namespace in Node.js as shown:

app.get("/", (req, res) => {
const tracer = appsignal.tracer();
tracer.withSpan(
tracer.createSpan({ namespace: "recommendations" }),
(span) => {
// code to measure goes here

span.close();
}
);
});

We’re up to three namespaces now.

The mobile and frontend teams may want to log errors in the dashboard. AppSignal’s JavaScript integration automatically assigns incoming data with the frontend namespace. But we can change it like this:

try {
// code that might fail
} catch (error) {
// handle the error

// send error to AppSignal
appsignal.sendError(error, {}, "Mobile");
}

After a while, data should start appearing in the Mobile namespace.

The example shows plain JavaScript, but there may be additional setup steps required if you’re using a frontend framework like React or Angular.

For the website, let’s try Ruby on Rails, a very well known MVC framework which AppSignal integrates with out-of-the-box. We’ll start the Rails controllers with the following snippet to set the namespace to homepage:

# in Rails we use before_action callback to change
# the namespace before the request starts
class HomepageController < ApplicationController
before_action :set_appsignal_namespace

def set_appsignal_namespace
Appsignal.set_namespace("homepage")
end

# controller actions ...
end

Next, we could use API endpoints to serve data to the website and mobile applications. For this, we could use Grape, a lightweight REST API framework for Ruby. This time, configuring AppSignal takes a bit more work.

After configuring the Ruby integration in ´config/appsignal.yml´, as we did earlier, you can start logging events and metrics with:

Appsignal.start_logger
Appsignal.start

Then, insert the AppSignal middleware in the error handler chain:

require "appsignal"
require "appsignal/integrations/grape"

class API < Grape::API
insert_before Grape::Middleware::Error, Appsignal::Grape::Middleware

resource :search do
desc 'return a product search'

before do
Appsignal.set_namespace("search")
end

get :product do

# product search logic

end
end
end

For more examples, check the Grape integration docs.

To complete the picture, we’ll end with a Sidekiq background job. Sidekiq is a popular job processor for Ruby and this is how we can start it up in standalone mode after configuring ´config/appsignal.yml´:

# config.ru
require 'appsignal'

Sidekiq.on(:startup) do
Appsignal.start
end

Sidekiq.on(:shutdown) do
Appsignal.stop('Sidekiq shutdown')
end

AppSignal automatically assigns data from jobs to the background namespace. We may want to change it to a more specific namespace.

require 'sidekiq'
require 'appsignal'

class PlainOldRuby
include Sidekiq::Worker

def perform()
Appsignal.set_namespace("urgent_background")

# job logic

end
end

## Collecting Metrics With the Standalone Agent

The standalone agent collects resource utilization metrics from any Ubuntu, RedHat, or CentOS machine. We can use the agent to monitor satellite servers that provide facilities like databases, gateways, or message brokers to the microservice applications.

In AppSignal we use the agent to monitor our own Kafka servers. The agent is also handy for creating custom instrumentation on languages and frameworks not directly supported.

To get started with the agent, download and install it following the installation instructions.

Once it’s running, we’ll need to edit the configuration file to set the API key, the application name, and the environment. Use the same values used in the rest of the microservice to get everything onto one dashboard.

# /etc/appsignal-agent.conf

push_api_key = "YOUR APPSIGNAL API KEY"
app_name = "Nozama"
environment = "production"


## Conclusion

Namespaces are a great way of organizing data distributed among multiple systems. They also allow us to configure notifications and fine-tune alert handling. To see how that works, check the first part of this series.