App-Device-Chip-sensor
view release on metacpan or search on metacpan
bin/device-chip-sensor-exporter view on Meta::CPAN
#!/usr/bin/perl
use v5.26;
use warnings;
use experimental 'signatures';
use utf8;
use Object::Pad 0.66;
use Future::AsyncAwait;
use Future::IO::Impl::IOAsync;
use Net::Prometheus;
use Metrics::Any::Adapter "Prometheus";
use Metrics::Any '$metrics';
our $metrics;
eval { require Future::AsyncAwait::Metrics };
$metrics->make_counter( chip_failures =>
name => "app_device_chip_sensor_chip_failures",
description => "Number of times a failure has been reported, per chip",
labels => [qw( chip )],
);
STDOUT->autoflush(1);
STDOUT->binmode( ":encoding(UTF-8)" );
my $PORT;
my $VERBOSE;
my $STRFTIME = "%Y-%m-%d %H:%M:%S";
class MyApp :isa(App::Device::Chip::sensor)
{
use Carp;
use POSIX qw( strftime );
use Scalar::Util qw( refaddr reftype );
method OPTSPEC
{
return $self->SUPER::OPTSPEC, (
'port=i' => \$PORT,
'verbose+' => \$VERBOSE,
'config|c=s' => sub { $self->read_config_file( $_[1] ); },
);
}
method read_config_file ( $path )
{
require YAML;
# Config file is YAML and can override a number of settings, and add chips
my $config = YAML::LoadFile( $path );
if( $config->{adapters} ) {
foreach my $c ( ( delete $config->{adapters} )->@* ) {
my $type = delete $c->{type} // croak "Expected adapter config to have a 'type'";
my $chips = delete $c->{chips} // croak "Expected adapter config to have a 'chips'";
# TODO: It'd be really nice if Device::Chip::Adapter->make( $type, %opts ) existed
my $desc = $type . join( ":", "", map { "$_=$c->{$_}" } sort keys %$c );
my $adapter = Device::Chip::Adapter->new_from_description( $desc );
foreach my $c ( $chips->@* ) {
my ( $type, %config ) = ( reftype($c)//"" eq "HASH" )
? ( delete $c->{type}, $c->%* )
: ( $c, () );
$self->add_chip(
type => $type,
adapter => $adapter,
mountopts => $config{mountopts},
config => $config{config},
);
}
}
}
if( exists $config->{port} ) {
$PORT = $config->{port};
delete $config->{port};
}
foreach my $opt (qw( interval best_effort filter )) {
exists $config->{$opt} and
$self->$opt = delete $config->{$opt};
}
# Legacy support
exists $config->{mid3} and
$self->filter = "mid3", delete $config->{mid3};
die "Unrecognised config file items " . join( ", ", sort keys %$config ) . "\n"
if keys %$config;
}
async method after_sensors :override ( @sensors )
{
my %got;
foreach my $sensor ( @sensors ) {
bin/device-chip-sensor-exporter view on Meta::CPAN
$units =~ s/%/percentage/g;
$units =~ s/°/degrees/g;
$units =~ s/µ/micro/g;
$units =~ s/m²/square_metre/g;
$units =~ s/m³/cubic_metre/g;
}
$got{$name}++ and next;
my $m = "make_" . $sensor->type;
$metrics->$m( $name,
name => join( "_", "sensor", $name, grep { defined } $units ),
description => "Sensor $name",
labels => [qw( chip )],
);
$metrics->inc_counter_by( chip_failures => 0, [ chip => $chipname ] );
}
}
# Count the failures per reading round
field %failures_per_chip;
# Counts in total
field %chip_failures;
method output_readings ( $now, $sensors, $values )
{
if( $VERBOSE ) {
printf "At %s\n", strftime( $STRFTIME, localtime );
$self->print_readings( $sensors, $values );
}
foreach my $i ( 0 .. $#$sensors ) {
my $sensor = $sensors->[$i];
my $value = $values->[$i];
my $chip = $sensor->chip;
my $chipname = ( ref $chip ) =~ s/^Device::Chip:://r;
my $m = ( $sensor->type eq "gauge" ) ? "set_gauge_to" :
( $sensor->type eq "counter" ) ? "inc_counter_by" :
die "Unrecognised sensor type";
$metrics->$m( $sensor->name,
$self->_format_reading( $sensor, $value ), [ chip => $chipname ] );
}
foreach my $chipaddr ( keys %failures_per_chip ) {
if( ++$chip_failures{ $chipaddr } >= 5 ) {
die "This sensor chip has failed 5 times in a row; aborting\n";
}
if( keys %chip_failures >= 3 ) {
die "Three or more sensor chips have failed; aborting\n";
}
}
undef %failures_per_chip;
}
method on_sensor_fail ( $sensor, $failure ) {
$self->SUPER::on_sensor_fail( $sensor, $failure );
my $chip = $sensor->chip;
my $chipname = ( ref $chip ) =~ s/^Device::Chip:://r;
$metrics->inc_counter( chip_failures => [ chip => $chipname ] );
$failures_per_chip{ refaddr $chip } = 1;
}
method on_sensor_ok ( $sensor ) {
delete $chip_failures{ refaddr $sensor->chip };
}
}
my $client = Net::Prometheus->new;
my $app = MyApp->new->parse_argv;
$client->export_to_IO_Async( undef, port => $PORT );
await $app->run;
( run in 0.732 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )