Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/dune
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
(library
(name prometheus_app_unix)
(public_name prometheus-app.unix)
(libraries prometheus prometheus-app cmdliner cohttp-lwt cohttp-lwt-unix)
(libraries prometheus prometheus-app cmdliner cohttp-lwt cohttp-lwt-unix logs.fmt fmt.tty)
(modules Prometheus_unix)
(wrapped false))
53 changes: 53 additions & 0 deletions app/prometheus_unix.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
open Prometheus

module Metrics = struct
let namespace = "prometheus"

let subsystem = "logs"

let inc_messages =
let help = "Total number of messages logged" in
let c =
Counter.v_labels ~label_names:[ "level"; "src" ] ~help ~namespace
~subsystem "messages_total"
in
fun lvl src ->
let lvl = Logs.level_to_string (Some lvl) in
Counter.inc_one @@ Counter.labels c [ lvl; src ]
end

module Unix_runtime = struct
let start_time = Unix.gettimeofday ()

Expand Down Expand Up @@ -53,3 +69,40 @@ let () =
let add (info, collector) =
CollectorRegistry.(register default) info collector in
List.iter add Unix_runtime.metrics

module Logging = struct
let inc_counter = Metrics.inc_messages

let pp_timestamp f x =
let open Unix in
let tm = localtime x in
Fmt.pf f "%04d-%02d-%02d %02d:%02d.%02d" (tm.tm_year + 1900) (tm.tm_mon + 1)
tm.tm_mday tm.tm_hour tm.tm_min tm.tm_sec

let reporter =
let report src level ~over k msgf =
let k _ = over (); k () in
let src = Logs.Src.name src in
Metrics.inc_messages level src;
msgf @@ fun ?header ?tags:_ fmt ->
Fmt.kpf k Fmt.stderr ("%a %a %a @[" ^^ fmt ^^ "@]@.")
pp_timestamp (Unix.gettimeofday ())
Fmt.(styled `Magenta string) (Printf.sprintf "%14s" src)
Logs_fmt.pp_header (level, header)
in
{ Logs.report = report }

let set_level (src, level) =
let rec aux = function
| [] -> Logs.warn (fun f -> f "set_level: logger %S not registered; ignoring" src)
| x :: _ when Logs.Src.name x = src -> Logs.Src.set_level x (Some level)
| _ :: xs -> aux xs
in
aux (Logs.Src.list ())

let init ?(default_level=Logs.Info) ?(levels=[]) () =
Fmt_tty.setup_std_outputs ();
Logs.set_reporter reporter;
Logs.set_level (Some default_level);
List.iter set_level levels
end
33 changes: 33 additions & 0 deletions app/prometheus_unix.mli
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,36 @@ val serve : config -> unit Lwt.t list
val opts : config Cmdliner.Term.t
(** [opts] is the extra command-line options to offer Prometheus
monitoring. *)

(** Report metrics for messages logged. *)
module Logging : sig
val init :
?default_level:Logs.level ->
?levels:(string * Logs.level) list ->
unit -> unit
(** Initialise the Logs library with a reporter that reports prometheus metrics too.
The reporter is configured to log to stderr and the log messages include a
timestamp and the event's source.

A server will typically use the following code to initialise logging:
{[
let () = Prometheus_app.Logging.init ()
]}

Or:
{[
let () =
Prometheus_unix.Logging.init ()
~default_level:Logs.Debug
~levels:[
"cohttp.lwt.io", Logs.Info;
]
]}
@param default_level The default log-level to use (default {!Logs.Info}).
@param levels Provides levels for specific log sources. *)

val inc_counter : Logs.level -> string -> unit
(** [inc_counter level src] increments the count of messages logged by [src] at [level].
The reporter installed by [init] calls this automatically, but you might want to
use this if you use your own reporter instead. *)
end
9 changes: 9 additions & 0 deletions examples/example.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ let main prometheus_config =

open Cmdliner

(* Optional: configure logging *)
let () =
Prometheus_unix.Logging.init ()
~default_level:Logs.Debug
~levels:[
"cohttp.lwt.io", Logs.Info;
]

let () =
Logs.info (fun f -> f "Logging initialised.");
print_endline "If run with the option --listen-prometheus=9090, this program serves metrics at\n\
http://localhost:9090/metrics";
let spec = Term.(const main $ Prometheus_unix.opts) in
Expand Down
42 changes: 22 additions & 20 deletions prometheus-app.opam
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
opam-version: "2.0"
synopsis: "Client library for Prometheus monitoring"
description: """\
Applications can enable metric reporting using the `prometheus-app` opam package.
This depends on cohttp and can serve the metrics collected above over HTTP.

The `prometheus-app.unix` ocamlfind library provides the `Prometheus_unix` module,
which includes a cmdliner option and pre-configured web-server.
See the `examples/example.ml` program for an example, which can be run as:

```shell
$ dune exec -- examples/example.exe --listen-prometheus=9090
If run with the option --listen-prometheus=9090, this program serves metrics at
http://localhost:9090/metrics
Tick!
Tick!
...
```

Unikernels can use `Prometheus_app` instead of `Prometheus_unix` to avoid the `Unix` dependency."""
maintainer: "talex5@gmail.com"
authors: ["Thomas Leonard" "David Scott"]
license: "Apache"
Expand All @@ -9,7 +27,7 @@ bug-reports: "https://github.com/mirage/prometheus/issues"
depends: [
"ocaml" {>= "4.02.3"}
"dune" {>= "1.0"}
"prometheus" {=version}
"prometheus" {= version}
"fmt"
"re"
"cohttp" {>= "1.0.0"}
Expand All @@ -18,29 +36,13 @@ depends: [
"lwt" {>= "2.5.0"}
"cmdliner"
"alcotest" {with-test}
"asetmap"
"astring"
"logs"
]
build: [
["dune" "subst"] {pinned}
["dune" "build" "-p" name "-j" jobs]
["dune" "runtest" "-p" name "-j" jobs] {with-test}
]
dev-repo: "git+https://github.com/mirage/prometheus.git"
description: """
Applications can enable metric reporting using the `prometheus-app` opam package.
This depends on cohttp and can serve the metrics collected above over HTTP.

The `prometheus-app.unix` ocamlfind library provides the `Prometheus_unix` module,
which includes a cmdliner option and pre-configured web-server.
See the `examples/example.ml` program for an example, which can be run as:

```shell
$ dune exec -- examples/example.exe --listen-prometheus=9090
If run with the option --listen-prometheus=9090, this program serves metrics at
http://localhost:9090/metrics
Tick!
Tick!
...
```

Unikernels can use `Prometheus_app` instead of `Prometheus_unix` to avoid the `Unix` dependency.
"""