Giter Club home page Giter Club logo

prometheus_client's Introduction

prometheus_client's People

Contributors

dependabot[bot] avatar fox32 avatar nachtmaar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

prometheus_client's Issues

add integration with the Angel3 framework

first i would like to thank you for your amazing work on this project, i've been working with dart for 7 years now developing fullstack applications in dart, using AngularDart and Angel Framework, and it's been a lot of pleasure using dart, before i used PHP/C# and javascript, although the dart has fewer tools and libraries with a small ecosystem because it is new, yet it has been very rewarding to work with dart because it is a much less verbose language than C# and PHP, I have contributed to several packages over the years based on what I'm going to need it, I read your article and was happy to see another developer using dart in the backend, dart has a lot of potential for the backend that is still untapped.

I still miss dart backend integration libraries with grafana/prometheus and seeing your work has left me with hope for an integration with Angel3

https://angel3-framework.web.app/#/about
https://github.com/dukefirehawk/angel
https://github.com/angulardart-community/angular

better integration with prometheus and grafana

I'm trying to implement a very simple rest API with just one endpoint using shelf_plus initializing multiple instances and I want to be able to integrate with Prometheus and Gratana to display a graph of requests per second and/or total requests per day, I made this implementation using the stream_isolate package and prometheus_client , I would like to know if there is a simpler and cleaner way to do this

import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:eloquent/eloquent.dart';
import 'package:stack_trace/stack_trace.dart';
import 'package:new_sali_backend/src/db/db_layer.dart';
import 'package:new_sali_backend/src/modules/protocolo/repositories/processo_repository.dart';
import 'package:new_sali_core/src/utils/core_utils.dart';
import 'package:new_sali_core/src/models/status_message.dart';
import 'package:shelf_plus/shelf_plus.dart';
import 'package:prometheus_client/prometheus_client.dart';
import 'package:prometheus_client/runtime_metrics.dart' as runtime_metrics;
import 'package:prometheus_client_shelf/shelf_metrics.dart' as shelf_metrics;
import 'package:prometheus_client/format.dart' as format;
import 'shelf_cors_headers_base.dart';
import 'stream_isolate.dart';

const defaultHeaders = {'Content-Type': 'application/json;charset=utf-8'};

Response responseError(String message,
    {dynamic exception, dynamic stackTrace, int statusCode = 400}) {
  final v = jsonEncode({
    'is_error': true,
    'status_code': statusCode,
    'message': message,
    'exception': exception?.toString(),
    'stackTrace': stackTrace?.toString()
  });
  return Response(statusCode, body: v, headers: defaultHeaders);
}

final basePath = '/api/v1';
final streamIsolates = <Map<int, BidirectionalStreamIsolate>>[];
void main(List<String> arguments) async {
  // Register default runtime metrics
  runtime_metrics.register();

  const numberOfIsolates = 3;

  for (var i = 0; i < numberOfIsolates - 1; i++) {
    final streamIsolate = await StreamIsolate.spawnBidirectional(isolateMain,
        debugName: i.toString(), argument: i);
    streamIsolates.add({i: streamIsolate});
    streamIsolate.stream.listen((event) => receiveAndPass(event, i));
  }
}

/// receive msg from isolate and send to all isolates
void receiveAndPass(event, int idx) {
  streamIsolates.forEach((item) {
    item.values.first.send(event);
  });
}

//xargs -I % -P 8 curl "http:/192.168.66.123:3161/api/v1/protocolo/processos/public/site/2023/10" < <(printf '%s\n' {1..400})
Stream isolateMain(Stream inc, id) {
  final streamController = StreamController.broadcast();

  final reg = CollectorRegistry();
  final http_requests_total = Counter(
      name: 'http_requests_total', help: 'Total number of http api requests');
  http_requests_total.register(reg);
  // listen msg from main
  inc.listen((msg) {
    http_requests_total.inc();
  });

  shelfRun(init([id, streamController, reg]),
      defaultShared: true,
      defaultBindAddress: '0.0.0.0',
      defaultBindPort: 3161);

  return streamController.stream;
}

Handler Function() init(List args) {
  var id = args[0] as int;
  var streamController = args[1] as StreamController;
  var reg = args[2] as CollectorRegistry;

  return () {
    final app = Router().plus;  
    app.use(shelf_metrics.register(reg));
    app.use(corsHeaders());
    app.use((innerHandler) {
      return (request) async {
        // Every time http_request is called, increase the counter by one
        final resp = await innerHandler(request);
        if (!request.url.path.contains('metrics')) {
          //send msg to main
          streamController.add('+1');
        }
        return resp;
      };
    });
    app.use(logRequestsCustom());

    routes(app, reg);
    return app;
  };
}

void routes(RouterPlus app, CollectorRegistry reg) {
  // Register a handler to expose the metrics in the Prometheus text format
  app.get('/metrics', () {
    return (request) async {
      final buffer = StringBuffer();
      final metrics = await reg.collectMetricFamilySamples();
      format.write004(buffer, metrics);
      return Response.ok(
        buffer.toString(),
        headers: {'Content-Type': format.contentType},
      );
    };
  });

  app.get('$basePath/protocolo/processos/public/site/<ano>/<codigo>',
      (Request request, String ano, String codigo) async {

    Connection? conn;
    try {
      final codProcesso = int.tryParse(codigo);
      if (codProcesso == null) {
        return responseError('codProcesso invalido');
      }
      final anoExercicio = ano;
      conn = await DBLayer().connect();
      final procRepo = ProcessoRepository(conn);
      final proc =
          await procRepo.getProcessoByCodigoPublic(codProcesso, anoExercicio);
      await conn.disconnect();
      return Response.ok(
        jsonEncode(proc, toEncodable: SaliCoreUtils.customJsonEncode),
        headers: defaultHeaders,
      );
    } catch (e, s) {
      await conn?.disconnect();
      print('public_backend@getProcessoByCodigoPublic $e $s');
      return responseError(StatusMessage.ERROR_GENERIC);
    }
  });
}

Middleware logRequestsCustom(
        {void Function(String message, bool isError)? logger}) =>
    (innerHandler) {
      final theLogger = logger ?? _defaultLogger;
      return (request) {
        var startTime = DateTime.now();
        var watch = Stopwatch()..start();
        return Future.sync(() => innerHandler(request)).then((response) {
          var msg = _message(startTime, response.statusCode,
              request.requestedUri, request.method, watch.elapsed);
          theLogger(msg, false);
          return response;
        }, onError: (Object error, StackTrace stackTrace) {
          if (error is HijackException) throw error;
          var msg = _errorMessage(startTime, request.requestedUri,
              request.method, watch.elapsed, error, stackTrace);
          theLogger(msg, true);
          // ignore: only_throw_errors
          throw error;
        });
      };
    };

String _formatQuery(String query) {
  return query == '' ? '' : '?$query';
}

String _message(DateTime requestTime, int statusCode, Uri requestedUri,
    String method, Duration elapsedTime) {
  return '${requestTime.toIso8601String()} '
      '${elapsedTime.toString().padLeft(15)} '
      '${method.padRight(7)} [$statusCode] ' // 7 - longest standard HTTP method
      '${requestedUri.path}${_formatQuery(requestedUri.query)}'
      '  isolate: ${Isolate.current.debugName}';
}

String _errorMessage(DateTime requestTime, Uri requestedUri, String method,
    Duration elapsedTime, Object error, StackTrace? stack) {
  var chain = Chain.current();
  if (stack != null) {
    chain = Chain.forTrace(stack)
        .foldFrames((frame) => frame.isCore || frame.package == 'shelf')
        .terse;
  }

  var msg = '$requestTime\t$elapsedTime\t$method\t${requestedUri.path}'
      '${_formatQuery(requestedUri.query)}\n$error';

  return '$msg\n$chain';
}

void _defaultLogger(String msg, bool isError) {
  if (isError) {
    print('[ERROR] $msg');
  } else {
    print(msg);
  }
}

inc() seems to go up by 2

I'm just writing my first exporter. I'm sure I'm just doing something stupid but everytime I make a request my Counter goes up by 2!

import 'package:prometheus_client/format.dart';
import 'package:prometheus_client/prometheus_client.dart';

class NutshellExporter {
  final Counter metricRequestCounter = Counter(
      name: 'my_new_counter', help: 'The total number of times I call inc');

  NutshellExporter() {
    metricRequestCounter.register();
  }

  Future<void> start() async {
    final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
    print('Listening on port 8080');

    await for (HttpRequest request in server) {
      metricRequestCounter.inc();
      request.response.headers.add('content-type', contentType);
      final metrics = await metricRequestCounter.collect();
      write004(request.response, metrics);
      await request.response.close();
    }
  }
}

1st response:

# HELP my_new_counter The total number of times I call inc
# TYPE my_new_counter counter
my_new_counter 1.0

2nd response:

# HELP my_new_counter The total number of times I call inc
# TYPE my_new_counter counter
my_new_counter 3.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.