Structuring Monitoring Data in Monolithic Applications With Namespaces

Tomas Tomas Fernandez on

What Are Namespaces?

Everything that happens in an AppSignal-monitored application is logged under a namespace. Namespaces work like folders, grouping events, issues, and monitoring data into manageable chunks.

By default, every application starts with three default namespaces: web, background, and frontend.

AppSignal maps incoming events using built-in per-application and per-integration rules. However, you can change these mappings at any time and even create new namespaces to model your application structure.

Trying Out Namespaces in Ruby

Let’s try out namespaces on a Ruby on Rails (ROR) application. After creating a fresh Rails project with rails new and setting up the rails integration, you’ll find the web namespace in your dashboard.

Dashboard showing web namespace

AppSignal shows the namespace as soon as it receives transactions from any of the controllers.

Most recent actions. Shows controller events.

The rest of the default namespaces won’t appear until there is some activity in them. Let’s add something in the background namespace to make things more interesting. This is how the dashboard looks after adding Sidekiq to the project (check the code in examples repository).

Dashboard showing the background namespace

AppSignal assigns the action to the background namespace because it recognizes Sidekiq as a job processor. AppSignal integrates with most of the popular background processors out there, but if yours is omitted, you can always add instrumentation to your jobs manually.

Last events in the background namespace

Creating Custom Namespaces

On large monolithic applications, the default namespaces can feel too generic. A big website typically serves static content, dynamic pages, API endpoints, among other web services. Most of this would all end up on the web namespace.

Also, every part of the application has a different priority. A login page problem is a lot more urgent than one in the internal administration panel. Yet AppSignal treats all issues within a namespace as equal. When there is a lot of activity, it can be hard to identify the most critical issues.

So, we should organize namespaces by priority and areas of responsibility. Then we can attach separate notification policies and alert only the interested parties. Following this reasoning, we could create custom namespaces for the login_page, api_endpoints, and admin_panel.

Custom namespaces let us create fine-grained zones for the application

To create a new namespace in Ruby, we’ll use Appsignal.set_namespace. Take a look at the following code, which creates a job in a namespace called urgent_background:

1
2
3
4
5
6
7
8
9
10
class FetchPricesWorker
    include Sidekiq::Worker

    def perform
        Appsignal.set_namespace("urgent_background")

        # worker code ...

    end
end

Once we made this change and restarted the app, these new jobs will appear in the newly-created namespace:

Dashboard showing the urgent\_background namespace

We can confirm that the actual job has been logged by checking the action name in the dashboard:

Last events in the urgent\_background namespace

Custom namespaces also work in all integrations.

Ignoring Namespaces

Another benefit of custom namespaces is that they let us disregard events from parts of the application we don’t care about. For instance, we may choose to ignore events from the admin_panel completely.

Ignoring a namespace takes three steps:

  1. Assign the parts we don’t want to monitor to a custom namespace.
  2. Configure the integration to ignore the namespace.
  3. Restart your app.

For Ruby, add the ignore_namespaces option in the AppSignal config file:

1
2
3
production:
  ignore_namespaces:
    - "admin_panel"

Ignoring a namespace skips all the transaction and span data at the source. Custom metric data is still reported.

The Elixir and JavaScript integrations have similar options. For more details check the ignoring namespaces guide.

Namespaces for Monolithic Applications

Now that we know how namespaces work, let’s examine a few ways we can use them to partition a monolithic application.

While there are no set rules, partitioning boils down to two strategies. You may choose one of them or a mix of both as a starting point:

Suppose that we have a controllers that handle user sign in and registration. When choosing to partition by role, we could map them to the user_login namespace.

1
2
3
4
5
6
7
8
9
10
11
12
# in Rails we use before_action callback to change
# the namespace before the request starts
class LoginController < ApplicationController
    before_action :set_appsignal_namespace

    def set_appsignal_namespace
        # Sets the namespace
        Appsignal.set_namespace("user_login")
    end

    # controller actions ...
end

But if you prefer using priority namespaces, a controller in charge of billing would probably go in the critical namespace.

1
2
3
4
5
6
7
class BillingPageController < ApplicationController
    before_action :set_appsignal_namespace

    def set_appsignal_namespace
        Appsignal.set_namespace("critical")
    end
end

Controllers that inherit from these share the same namespace as their parents:

1
2
3
4
5
6
7
8
# any controllers that inherit from LoginController
# are also part of the "user_login" namespace
class RegistrationController < LoginController

    # there’s no need for before_action here
    # this controller already reports to the parent’s namespace

end

As we’ve seen, jobs and tasks are automatically assigned to the background namespace. Whenever possible, we should assign them into more specific ones. For instance, a database cleanup job could go into the database namespace, like this:

1
2
3
4
5
class ActiveJobDatabaseCleanupJob < ActiveJob::Base
  queue_as :default

  def perform(argument = nil, options = {})
    Appsignal.set_namespace("database")

Priorities also work for jobs. We could assign unimportant tasks to low for instance, as in this Rake task:

1
2
3
4
5
6
7
8
task :unimportant_job do

  # Run this rake job in the low namespace
  Appsignal.set_namespace("low")

  # job code ...

end

In some cases, you will want to log actions using a manual transaction. You can define the namespace while creating it, like in the following example, which codes a custom mailer job:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Job
    def perform

        # Create a transaction for this job and set the namespace
        transaction = Appsignal::Transaction.create(
            SecureRandom.uuid,
            "mailer",
            Appsignal::Transaction::GenericRequest.new(ENV.to_hash)
        )

        # job code ...

    end
end

Namespaces and Notifications

Not everyone in the team needs to be notified about every problem. Frontend specialists don’t care about background jobs as much as backend developers. Still, they may want to know when there’s a problem in the backend. Backend developers will surely like to be notified of performance issues on the web namespace. Namespaces let us route notifications to the right people.

Setting Up Per-Namespace Notifiers

We can create notification groups that are only active for specific namespaces. For instance, we could send emails for errors in the web namespace, or send a message into the #frontend Slack channel for issues in the frontend namespace.

To create per-namespace notification groups, go to App Settings > Notifications > Notifiers and click on Add Integration.

Adding an integration

Select one of the integrations and type its name. Choose which type of messages to send and for which namespace. For example, let’s create a Slack notification for the #frontend channel.

slack integration

While we’re still here, create a second notification for the backend developers:

New notification group for web

You can configure as many notifiers as you need to keep the team up to date with everything that’s happening.

Notifiers for different parts of the application

Changing Per-Namespace Notifications

When an incident is created, AppSignal will apply a notification policy. This policy is based on the namespace the error comes from. We can define separate policies for each namespace.

To see the namespace defaults for your application, go to App Settings > Notifications > Namespace defaults.

Namespace defaults for web, background and urgent\_background

Here, you’ll find options to customize error and performance notifications for every namespace:

Creating Per-Namespace Triggers

Triggers tell AppSignal to create an incident and send notifications when a metric goes over or below a predefined value. Since different parts of an application may have different thresholds, we should create separate triggers for each namespace. The classic example is a trigger that alerts us when throughput is too low in the web namespace.

To create a trigger, go to Anomaly Detection > Triggers, and click on Add your first trigger.

Select an Actions trigger type on the left menu and choose the relevant namespace. Then, set the thresholds that trigger the alert.

Creating a new trigger for the web namespace

Here you can also define which groups should be notified. To finalize, click on Save Trigger.

This alert will send a notification to backend developers

Conclusion

Namespaces let make sense of your application’s monitoring data. They are also indispensable for firing notifications and incidents on a fine-grained level, limiting noise, and avoiding false positives.

After checking how custom namespaces work on Ruby, Node.js, and Elixir, read these next to continue learning how to use namespaces:

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.

10 latest articles

Go back
Ruby magic icon

Subscribe to

Ruby Magic

Magicians never share their secrets. But we do. Sign up for our Ruby Magic email series and receive deep insights about garbage collection, memory allocation, concurrency and much more.

We'd like to set cookies, read why.