Basic Cumulocity IoT Microservice Monitoring

Basic Cumulocity IoT Microservice Monitoring

Overview

Once you deploy a Microservice on the Cumulocity Platform, the Status tab of the Microservice in the Administration application can show you whether it is active or not. And the health endpoint can also help you to monitor the status of the Microservice.

However, if state monitoring does not fulfill your requirements for microservice monitoring, you need to monitor a wider variety of detailed system statistics, how can you easily access this data from a microservice hosted on the Cumulocity IoT Platform? This article will show you a basic approach to monitor your Microservice directly on the platform.

Idea

This example is based on the Python Microservice example of C8Y. A dummy device registered to the platform will be used to display the data from the Microservice. Right after the system statistics are obtained within the Microservice, they will be sent to the device. The connection between the microservice and the device can be checked or established in the Microservice. When the Microservice is running, the users with the right permissions can monitor it in the Device Management application like a device or even create a dashboard for it in the Cockpit application.

Pros & Cons of basic Microservice Monitoring

Pros

  • No additional infrastructure or tools needed

  • No additional maintenance required

  • Take advantage of platform features for visualization and alerting

  • Take advantage of Cumulocity SDK

Cons

  • Microservices with “single-tenant” isolation can be only monitored in the same tenant they are deployed

  • Not suitable for multiple Microservices monitoring

  • Metrics must be converted to the Cumulocity-acceptable form

  • Dependent on Cumulocity infrastructure - no monitoring/alerting possible if instance/tenant is down.

This approach is recommended when:

  • you have a small number of Microservices in one tenant and want to monitor them on the platform directly

  • you want to monitor a few metrics of the Microservices

  • 3rd party monitoring tools are not available to you

If you have a large number of Microservices subscribed to multiple tenants, or you want to obtain a more professional and comprehensive monitoring of the Microservices, here is an advanced monitoring approach that may be more meaningful for you.

Details

Step 1: Bootstrap Service to the Device

The external ID and the type are the key values that link the service to the device. External ID is the same as the name of the service in order to make sure that the connection is unique. The first step aims to find the device with the required external ID and to identify its internal device ID for the following steps. If there isn’t any matching device, a new device will be created and the required external ID will be assigned to it.

c8y = CumulocityApi( base_url = os.environ.get('C8Y_BASEURL'), tenant_id= os.environ.get('C8Y_TENANT'), username = os.environ.get('C8Y_USER'), password= os.environ.get('C8Y_PASSWORD') )

ms_device_name = os.environ.get('APPLICATION_NAME') device_type = 'service_device' external_type = 'c8y_ServiceId' device_id = None

def bootstrapServiceDevice(): global device_id try: logger.info('Finding the device connected to the service') device_id = c8y.identity.get_id(external_id=ms_device_name, external_type=external_type) except KeyError: logger.info('Device not found, creating new device') ms_device = Device(c8y,device_type,ms_device_name).create() device_id = ms_device.id c8y.identity.create(external_id=ms_device_name, external_type=external_type, managed_object_id=device_id) logger.info(f'Device created, Device ID: {device_id}') except ValueError: logger.error('Invalid credentials') sys.exit()

In this step, the identity function of the Cumulocity Python API is used to find and create the external ID, and the Device method is used to create the device.

Step 2: Collect metrics

The functions from psutil python library which are used for gathering the system statistics return different kinds of results. As an example, we chose to monitor the CPU utility and thread count of the Microservice. All data needs to be converted to the Cumulocity-acceptable JSON format. In this basic monitoring use case, we focus on the Microservice itself. Therefore, the statistics come from the process in which the Microservice is running.

request_count = 0 task_queue = queue.Queue()

def collectMetrics(): while True: num_threads = psutil.Process(os.getpid()).num_threads() metrics_dict = { 'cpu_percent': {}, 'num_threads': {'num_threads': {'value': num_threads}}, 'task_queue_size':{'task_queue_size': {'value': task_queue.qsize()}} } cpu_num = psutil.Process(os.getpid()).cpu_num() cpu_percent = psutil.cpu_percent(interval=1, percpu=True) metrics_dict['cpu_percent']['cpu'] = {'value':cpu_percent[cpu_num]} if not task_queue.empty(): task_queue.get() metrics_dict.update({'request_count': {'request_count': {'value': request_count}}}) for key, value in metrics_dict.items(): exec('Measurement(c8y, type='c8y_Monitoring', source=device_id, {}={}).create()'.format('c8y_'+key,value)) if task_queue.empty(): time.sleep(10)

In addition, we set a dummy task list and an endpoint for the service. Once the endpoint is touched, a new task will be added to the list and the usage of the endpoint will also be counted. These self-defined variables will also be part of the metrics to be sent to the platform. In this way, you can monitor the Microservice with common system statistics and any kind of variable you need.

app = Flask(name)

@app.route('/') def add_metrics(): global request_count request_count += 1 task_queue.put('A new task has been added') return "Added new task!"

Step 3: Monitor the Microservice on the platform

After deploying this Microservice on the platform, a new device will be created to display the data from the service. You can monitor it and check the history data in the Device Management application. For even more convenient monitoring, you can add a dashboard for this device in the Cockpit application. In the Cockpit, Smart Rules can support your monitoring by rising alarms or sending emails. Other features of the Cumulocity IoT Platform can also be used for monitoring your Microservice just like monitoring a device.

Screenshot 2023-03-02 at 13.36.19

Conclusion

Here is the entire Microservice python code. When you create your very first Microservice, you can already monitor it without much effort. As there is no knowledge requirement for additional monitoring applications. This example shows you an easy approach to monitor the Microservice on the platform, but it is more suitable for monitoring a Microservice with single-tenant isolation level. If you want to learn more about monitoring of multiple Microservices or multi-tenant Microservices please check out this article.

import logging import sys, os, time from c8y_api import CumulocityApi from c8y_api.model import Device, Measurement, Event import psutil import queue import threading from flask import Flask

logger = logging.getLogger('') logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger.info('Logger was initialized')

c8y = CumulocityApi( base_url = os.environ.get('C8Y_BASEURL'), tenant_id= os.environ.get('C8Y_TENANT'), username = os.environ.get('C8Y_USER'), password= os.environ.get('C8Y_PASSWORD') )

ms_device_name = os.environ.get('APPLICATION_NAME') device_type = 'service_device' external_type = 'c8y_ServiceId' device_id = None

request_count = 0 task_queue = queue.Queue()

def bootstrapServiceDevice(): global device_id try: logger.info('Finding the device connected to the service') device_id = c8y.identity.get_id(external_id=ms_device_name, external_type=external_type) except KeyError: logger.info('Device not found, creating new device') ms_device = Device(c8y,device_type,ms_device_name).create() device_id = ms_device.id c8y.identity.create(external_id=ms_device_name, external_type=external_type, managed_object_id=device_id) logger.info(f'Device created, Device ID: {device_id}') except ValueError: logger.error('Invalid credentials') sys.exit()

def collectMetrics(): while True: num_threads = psutil.Process(os.getpid()).num_threads() metrics_dict = { 'cpu_percent': {}, 'num_threads': {'num_threads': {'value': num_threads}}, 'task_queue_size':{'task_queue_size': {'value': task_queue.qsize()}} } cpu_num = psutil.Process(os.getpid()).cpu_num() cpu_percent = psutil.cpu_percent(interval=1, percpu=True) metrics_dict['cpu_percent']['cpu'] = {'value':cpu_percent[cpu_num]} if not task_queue.empty(): task_queue.get() metrics_dict.update({'request_count': {'request_count': {'value': request_count}}}) for key, value in metrics_dict.items(): exec('Measurement(c8y, type='c8y_Monitoring', source=device_id, {}={}).create()'.format('c8y_'+key,value)) if task_queue.empty(): time.sleep(10)

app = Flask(name)

@app.route('/') def add_metrics(): global request_count request_count += 1 task_queue.put('A new task has been added') return "Added new task!"

if name == 'main': bootstrapServiceDevice() Event(c8y, type='service_event', source=device_id, time='now', text='Service started').create() t1 = threading.Thread(target=collectMetrics, args=()) t1.start() app.run(host='0.0.0.0', port=80)

Read full topic