appsignal

Microservices Monitoring: Using Namespaces for Data Structuring

Tomas Fernandez

Tomas Fernandez on

Microservices Monitoring: Using Namespaces for Data Structuring

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:

Monolith Design

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.

Microservice Design

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.

Every microservice reports to 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”:

1# config/appsignal.yml
2production:
3  active: true
4  push_api_key: "YOUR APPSIGNAL API KEY"
5  name: "Nozama"

Which is very similar to how we configure Elixir integrations:

1# config/config.exs
2config :appsignal, :config,
3  active: true,
4  name: "Nozama",
5  push_api_key: "YOUR APPSIGNAL API KEY",
6  env: "production"

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

1const { Appsignal } = require("@appsignal/nodejs");
2
3const appsignal = new Appsignal({
4  active: true,
5  name: "Nozama",
6  apiKey: "YOUR APPSIGNAL API KEY",
7});

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

1import Appsignal from "@appsignal/javascript";
2
3export default new Appsignal({
4  name: "Nozama",
5  key: "YOUR FRONTEND API KEY",
6});

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:

1# in a Phoenix controller, we use plug to run the namespace initialization
2defmodule BillingPageController.PageController do
3  use BillingPageController, :controller
4
5  plug :set_appsignal_namespace
6
7  defp set_appsignal_namespace(conn, _params) do
8    # Sets all actions in this controller to report in the "billing" namespace
9    Appsignal.Transaction.set_namespace(:billing)
10    conn
11  end
12
13  # rest of the controller ...
14end

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

Showing the billing namespace

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:

1defmodule PayButtonController.PageController do
2  use PayButtonController, :controller
3
4  plug :set_appsignal_namespace
5
6  defp set_appsignal_namespace(conn, _params) do
7    Appsignal.Span.set_namespace(Appsignal.Tracer.root_span(), "pay_button")
8    conn
9  end
10
11  # rest of the controller ...
12
13end

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.

Added pay<sub>button</sub> namespace

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:

1app.get("/", (req, res) => {
2  const tracer = appsignal.tracer();
3  tracer.withSpan(
4    tracer.createSpan({ namespace: "recommendations" }),
5    (span) => {
6      // code to measure goes here
7
8      span.close();
9    }
10  );
11});

We’re up to three namespaces now.

Recommendation service

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:

1try {
2  // code that might fail
3} catch (error) {
4  // handle the error
5
6  // send error to AppSignal
7  appsignal.sendError(error, {}, "Mobile");
8}

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

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:

1# in Rails we use before_action callback to change
2# the namespace before the request starts
3class HomepageController < ApplicationController
4    before_action :set_appsignal_namespace
5
6    def set_appsignal_namespace
7        Appsignal.set_namespace("homepage")
8    end
9
10    # controller actions ...
11end

Search API and homepage

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:

1Appsignal.start_logger
2Appsignal.start

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

1require "appsignal"
2require "appsignal/integrations/grape"
3
4class API < Grape::API
5    insert_before Grape::Middleware::Error, Appsignal::Grape::Middleware
6
7
8    resource :search do
9      desc 'return a product search'
10
11      before do
12        Appsignal.set_namespace("search")
13      end
14
15      get :product do
16
17        # product search logic
18
19      end
20  end
21end

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´:

1# config.ru
2require 'appsignal'
3
4Sidekiq.on(:startup) do
5  Appsignal.start
6end
7
8Sidekiq.on(:shutdown) do
9  Appsignal.stop('Sidekiq shutdown')
10end

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

1require 'sidekiq'
2require 'appsignal'
3
4
5class PlainOldRuby
6  include Sidekiq::Worker
7
8  def perform()
9    Appsignal.set_namespace("urgent_background")
10
11    # job logic
12
13  end
14end

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.

1# /etc/appsignal-agent.conf
2push_api_key = "YOUR APPSIGNAL API KEY"
3app_name = "Nozama"
4environment = "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.

Additional reads:

Share this article

RSS
Tomas Fernandez

Tomas Fernandez

Our guest author Tomas spent 10 years working at IBM, where he did a bit of everything - development, service delivery, database administration, and cloud engineering. He’s now an independent consultant and a technical writer.

All articles by Tomas Fernandez

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps