Added a bit more counters and metrics

This commit is contained in:
Gleb Tcivie 2024-06-24 22:56:49 +03:00
parent 66e5c56fe9
commit cfd5bfc098
2 changed files with 204 additions and 18 deletions

View file

@ -1,7 +1,8 @@
import os
import redis
from meshtastic.mesh_pb2 import MeshPacket
from meshtastic.config_pb2 import Config
from meshtastic.mesh_pb2 import MeshPacket, HardwareModel
from prometheus_client import CollectorRegistry, Counter
from exporter.registry import ProcessorRegistry, ClientDetails
@ -27,12 +28,12 @@ class MessageProcessor:
source_client_details = self._get_client_details(getattr(mesh_packet, 'from'))
if os.getenv('MESH_HIDE_SOURCE_DATA', 'false') == 'true':
source_client_details = ClientDetails(node_id=source_client_details['id'], short_name='Hidden',
source_client_details = ClientDetails(node_id=source_client_details.node_id, short_name='Hidden',
long_name='Hidden')
destination_client_details = self._get_client_details(getattr(mesh_packet, 'to'))
if os.getenv('MESH_HIDE_DESTINATION_DATA', 'false') == 'true':
destination_client_details = ClientDetails(node_id=destination_client_details['id'], short_name='Hidden',
destination_client_details = ClientDetails(node_id=destination_client_details.node_id, short_name='Hidden',
long_name='Hidden')
if port_num in map(int, os.getenv('FILTERED_PORTS', '1').split(',')): # Filter out ports
@ -62,6 +63,11 @@ class MessageProcessor:
def _get_client_details(self, node_id: str) -> ClientDetails:
details = self.redis_client.hgetall(f"node:{node_id}")
if details:
return ClientDetails(node_id=node_id, short_name=details['short_name'], long_name=details['long_name'])
return ClientDetails(node_id=node_id,
short_name=details.get('short_name', 'Unknown'),
long_name=details.get('long_name', 'Unknown'),
hardware_model=details.get('hardware_model', HardwareModel.UNSET),
role=details.get('role', Config.DeviceConfig.Role.ValueType.UNSET),
)
return ClientDetails(node_id=node_id, short_name='Unknown', long_name='Unknown')
return ClientDetails(node_id=node_id)

View file

@ -1,3 +1,4 @@
import json
import os
from abc import ABC, abstractmethod
from venv import logger
@ -5,27 +6,118 @@ from venv import logger
import redis
import unishox2
from meshtastic.admin_pb2 import AdminMessage
from meshtastic.mesh_pb2 import Position, User, Routing, Waypoint, RouteDiscovery, NeighborInfo
from meshtastic.config_pb2 import Config
from meshtastic.mesh_pb2 import Position, User, Routing, Waypoint, RouteDiscovery, NeighborInfo, HardwareModel
from meshtastic.mqtt_pb2 import MapReport
from meshtastic.paxcount_pb2 import Paxcount
from meshtastic.portnums_pb2 import PortNum
from meshtastic.remote_hardware_pb2 import HardwareMessage
from meshtastic.storeforward_pb2 import StoreAndForward
from meshtastic.telemetry_pb2 import Telemetry
from prometheus_client import CollectorRegistry, Counter
from meshtastic.telemetry_pb2 import Telemetry, DeviceMetrics, EnvironmentMetrics, AirQualityMetrics, PowerMetrics
from prometheus_client import CollectorRegistry, Counter, Gauge
class _Metrics:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(_Metrics, cls).__new__(cls)
return cls._instance
def __init__(self, registry: CollectorRegistry):
if not hasattr(self, 'initialized'): # Ensuring __init__ runs only once
self._registry = registry
self._init_metrics()
self.initialized = True # Attribute to indicate initialization
def _init_metrics(self): # TODO: Go over the metrics and rethink some of them to be more like the longtitute and
# latitude - The values should represent something and we shouldn't just label stuff. Also, the labels should
# be less used looked upon like keys for the data
self.message_counter = Counter(
'text_message_app',
'Text message app payload details',
['client_id', 'short_name', 'long_name', 'message_content'],
registry=self._registry
)
self.telemetry_counter_device_metrics = Counter(
'telemetry_app_device_metrics',
'Telemetry app payload details - Device metrics',
['client_id', 'short_name', 'long_name'
'battery_level', 'voltage', 'channel_utilization', 'air_until_tx',
'uptime_seconds'
],
registry=self._registry
)
self.telemetry_counter_environment_metrics = Counter(
'telemetry_app_environment_metrics',
'Telemetry app payload details - Environment metrics',
['client_id', 'short_name', 'long_name'
'temperature', 'relative_humidity', 'barometric_pressure', 'gas_resistance',
'voltage', 'current', 'iaq', 'distance', 'lux', 'white_lux', 'ir_lux', 'uv_lux', 'wind_direction',
'wind_speed', 'weight'
],
registry=self._registry
)
self.telemetry_counter_air_quality_metrics = Counter(
'telemetry_app_air_quality_metrics',
'Telemetry app payload details - Air quality metrics',
['client_id', 'short_name', 'long_name'
'pm10_standard', 'pm25_standard', 'pm100_standard', 'pm10_environmental',
'pm25_environmental', 'pm100_environmental', 'particles_03um', 'particles_05um', 'particles_10um',
'particles_25um', 'particles_50um', 'particles_100um'
],
registry=self._registry
)
self.telemetry_counter_power_metrics = Counter(
'telemetry_app_power_metrics',
'Telemetry app payload details - Power metrics',
['client_id', 'short_name', 'long_name'
'ch1_voltage', 'ch1_current', 'ch2_voltage', 'ch2_current', 'ch3_voltage',
'ch3_current'
],
registry=self._registry
)
self.device_latitude_gauge = Gauge(
'device_latitude',
'Device latitude',
['client_id', 'short_name', 'long_name'],
registry=self._registry
)
self.device_longitude_gauge = Gauge(
'device_longitude',
'Device longitude',
['client_id', 'short_name', 'long_name'],
registry=self._registry
)
self.device_altitude_gauge = Gauge(
'device_altitude',
'Device altitude',
['client_id', 'short_name', 'long_name'],
registry=self._registry
)
self.device_position_precision_gauge = Gauge(
'device_position_precision',
'Device position precision',
['client_id', 'short_name', 'long_name'],
registry=self._registry
)
class ClientDetails:
def __init__(self, node_id, short_name, long_name):
def __init__(self, node_id, short_name='Unknown', long_name='Unknown', hardware_model=HardwareModel.UNSET,
role=None):
self.node_id = node_id
self.short_name = short_name
self.long_name = long_name
self.hardware_model: HardwareModel = hardware_model
self.role: Config.DeviceConfig.Role = role
class Processor(ABC):
def __init__(self, registry: CollectorRegistry, redis_client: redis.Redis):
self.registry = registry
self.redis_client = redis_client
self.metrics = _Metrics(registry)
@abstractmethod
def process(self, payload: bytes, client_details: ClientDetails):
@ -59,19 +151,13 @@ class UnknownAppProcessor(Processor):
class TextMessageAppProcessor(Processor):
def __init__(self, registry: CollectorRegistry, redis_client: redis.Redis):
super().__init__(registry, redis_client)
self.message_counter = Counter(
'text_message_app',
'Text message app payload details',
['client_id', 'short_name', 'long_name', 'message_content'],
registry=self.registry
)
def process(self, payload: bytes, client_details: ClientDetails):
logger.debug("Received TEXT_MESSAGE_APP packet")
message = payload.decode('utf-8')
if os.getenv('HIDE_MESSAGE', 'true') == 'true':
message = 'Hidden'
self.message_counter.labels(
self.metrics.message_counter.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name,
@ -94,6 +180,26 @@ class PositionAppProcessor(Processor):
logger.debug("Received POSITION_APP packet")
position = Position()
position.ParseFromString(payload)
self.metrics.device_latitude_gauge.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name
).set(position.latitude_i)
self.metrics.device_longitude_gauge.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name
).set(position.longitude_i)
self.metrics.device_altitude_gauge.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name
).set(position.altitude)
self.metrics.device_position_precision_gauge.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name
).set(position.position_precision)
pass
@ -103,6 +209,15 @@ class NodeInfoAppProcessor(Processor):
logger.debug("Received NODEINFO_APP packet")
user = User()
user.ParseFromString(payload)
user_details = {
'short_name': user.short_name,
'long_name': user.long_name,
'id': user.id,
'hardware_model': user.hardware_model,
'role': user.role,
}
user_details_json = json.dumps(user_details)
self.redis_client.set(f"node:{client_details.node_id}", user_details_json)
pass
@ -207,7 +322,72 @@ class TelemetryAppProcessor(Processor):
logger.debug("Received TELEMETRY_APP packet")
telemetry = Telemetry()
telemetry.ParseFromString(payload)
pass
if telemetry.HasField('device_metrics'):
device_metrics: DeviceMetrics = telemetry.device_metrics
self.metrics.telemetry_counter_device_metrics.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name,
battery_level=device_metrics.battery_level,
voltage=device_metrics.voltage,
channel_utilization=device_metrics.channel_utilization,
air_until_tx=device_metrics.air_until_tx,
uptime_seconds=device_metrics.uptime_seconds
).inc()
if telemetry.HasField('environment_metrics'):
environment_metrics: EnvironmentMetrics = telemetry.environment_metrics
self.metrics.telemetry_counter_environment_metrics.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name,
temperature=environment_metrics.temperature,
relative_humidity=environment_metrics.relative_humidity,
barometric_pressure=environment_metrics.barometric_pressure,
gas_resistance=environment_metrics.gas_resistance,
voltage=environment_metrics.voltage,
current=environment_metrics.current,
iaq=environment_metrics.iaq,
distance=environment_metrics.distance,
lux=environment_metrics.lux,
white_lux=environment_metrics.white_lux,
ir_lux=environment_metrics.ir_lux,
uv_lux=environment_metrics.uv_lux,
wind_direction=environment_metrics.wind_direction,
wind_speed=environment_metrics.wind_speed,
weight=environment_metrics.weight
).inc()
if telemetry.HasField('air_quality_metrics'):
air_quality_metrics: AirQualityMetrics = telemetry.air_quality_metrics
self.metrics.telemetry_counter_air_quality_metrics.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name,
pm10_standard=air_quality_metrics.pm10_standard,
pm25_standard=air_quality_metrics.pm25_standard,
pm100_standard=air_quality_metrics.pm100_standard,
pm10_environmental=air_quality_metrics.pm10_environmental,
pm25_environmental=air_quality_metrics.pm25_environmental,
pm100_environmental=air_quality_metrics.pm100_environmental,
particles_03um=air_quality_metrics.particles_03um,
particles_05um=air_quality_metrics.particles_05um,
particles_10um=air_quality_metrics.particles_10um,
particles_25um=air_quality_metrics.particles_25um,
particles_50um=air_quality_metrics.particles_50um,
particles_100um=air_quality_metrics.particles_100um
).inc()
if telemetry.HasField('power_metrics'):
power_metrics: PowerMetrics = telemetry.power_metrics
self.metrics.telemetry_counter_power_metrics.labels(
client_id=client_details.node_id,
short_name=client_details.short_name,
long_name=client_details.long_name,
ch1_voltage=power_metrics.ch1_voltage,
ch1_current=power_metrics.ch1_current,
ch2_voltage=power_metrics.ch2_voltage,
ch2_current=power_metrics.ch2_current,
ch3_voltage=power_metrics.ch3_voltage,
ch3_current=power_metrics.ch3_current
).inc()
@ProcessorRegistry.register_processor(PortNum.ZPS_APP)