mirror of
https://github.com/ocaml-tracing/ocaml-opentelemetry.git
synced 2026-03-08 03:47:59 -04:00
initial commit
This commit is contained in:
commit
14a0fa922d
20 changed files with 470 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
_build
|
||||
_opam
|
||||
*.json
|
||||
*.gz
|
||||
*.db
|
||||
.merlin
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "vendor/opentelemetry-proto"]
|
||||
path = vendor/opentelemetry-proto
|
||||
url = https://github.com/open-telemetry/opentelemetry-proto
|
||||
14
Makefile
Normal file
14
Makefile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
OPTS=--profile=release
|
||||
all:
|
||||
@dune build @all $(OPTS)
|
||||
|
||||
test:
|
||||
@dune runtest --force $(OPTS)
|
||||
|
||||
clean:
|
||||
@dune clean
|
||||
|
||||
WATCH ?= @all
|
||||
watch:
|
||||
@dune build $(WATCH) -w $(OPTS)
|
||||
24
README.md
Normal file
24
README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
# Opentelemetry
|
||||
|
||||
This project provides an API for instrumenting server software
|
||||
using [opentelemetry](https://opentelemetry.io/docs), as well as
|
||||
connectors to talk to opentelemetry software such as [jaeger](https://www.jaegertracing.io/).
|
||||
|
||||
## Use
|
||||
|
||||
- [ ] TODO: basic traces
|
||||
- [ ] TODO: interface with `logs` (carry context around)
|
||||
- [ ] TODO: interface with `lwt`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Semantic Conventions
|
||||
|
||||
Not supported yet.
|
||||
|
||||
- [ ] [metrics](https://opentelemetry.io/docs/reference/specification/metrics/semantic_conventions/)
|
||||
- [ ] [traces](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/)
|
||||
- [ ] [resources](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/)
|
||||
3
dune
Normal file
3
dune
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
(env
|
||||
(_ (flags :standard -warn-error -a+8)))
|
||||
32
dune-project
Normal file
32
dune-project
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
(lang dune 2.3)
|
||||
(name opentelemetry)
|
||||
(generate_opam_files true)
|
||||
(source
|
||||
(github aestheticintegration/ocaml-opentelemetry))
|
||||
|
||||
(authors "the Imandra team")
|
||||
(maintainers "the Imandra team")
|
||||
(license MIT)
|
||||
;(documentation https://url/to/documentation)
|
||||
|
||||
(package
|
||||
(name opentelemetry)
|
||||
(synopsis "Instrumentation for https://opentelemetry.io")
|
||||
(depends
|
||||
(ocaml (>= "4.08"))
|
||||
(dune (>= "2.3"))
|
||||
(ocaml-protoc (>= 2.1)))
|
||||
(depopts
|
||||
ptime)
|
||||
(tags
|
||||
(instrumentation tracing opentelemetry datadog jaeger)))
|
||||
|
||||
(package
|
||||
(name opentelemetry-client-ocurl)
|
||||
(depends
|
||||
(ocaml (>= "4.08"))
|
||||
(dune (>= "2.3"))
|
||||
(opentelemetry (= :version))
|
||||
(ocaml-protoc (>= 2.1))
|
||||
ocurl)
|
||||
(synopsis "Collector client for opentelemetry, using http + ocurl"))
|
||||
32
opentelemetry-client-ocurl.opam
Normal file
32
opentelemetry-client-ocurl.opam
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# This file is generated by dune, edit dune-project instead
|
||||
opam-version: "2.0"
|
||||
synopsis: "Collector client for opentelemetry, using http + ocurl"
|
||||
maintainer: ["the Imandra team"]
|
||||
authors: ["the Imandra team"]
|
||||
license: "MIT"
|
||||
homepage: "https://github.com/aestheticintegration/ocaml-opentelemetry"
|
||||
bug-reports:
|
||||
"https://github.com/aestheticintegration/ocaml-opentelemetry/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.08"}
|
||||
"dune" {>= "2.3"}
|
||||
"opentelemetry" {= version}
|
||||
"ocaml-protoc" {>= "2.1"}
|
||||
"ocurl"
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {pinned}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
"-p"
|
||||
name
|
||||
"-j"
|
||||
jobs
|
||||
"@install"
|
||||
"@runtest" {with-test}
|
||||
"@doc" {with-doc}
|
||||
]
|
||||
]
|
||||
dev-repo:
|
||||
"git+https://github.com/aestheticintegration/ocaml-opentelemetry.git"
|
||||
32
opentelemetry.opam
Normal file
32
opentelemetry.opam
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# This file is generated by dune, edit dune-project instead
|
||||
opam-version: "2.0"
|
||||
synopsis: "Instrumentation for https://opentelemetry.io"
|
||||
maintainer: ["the Imandra team"]
|
||||
authors: ["the Imandra team"]
|
||||
license: "MIT"
|
||||
tags: ["instrumentation" "tracing" "opentelemetry" "datadog" "jaeger"]
|
||||
homepage: "https://github.com/aestheticintegration/ocaml-opentelemetry"
|
||||
bug-reports:
|
||||
"https://github.com/aestheticintegration/ocaml-opentelemetry/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.08"}
|
||||
"dune" {>= "2.3"}
|
||||
"ocaml-protoc" {>= "2.1"}
|
||||
]
|
||||
depopts: ["ptime"]
|
||||
build: [
|
||||
["dune" "subst"] {pinned}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
"-p"
|
||||
name
|
||||
"-j"
|
||||
jobs
|
||||
"@install"
|
||||
"@runtest" {with-test}
|
||||
"@doc" {with-doc}
|
||||
]
|
||||
]
|
||||
dev-repo:
|
||||
"git+https://github.com/aestheticintegration/ocaml-opentelemetry.git"
|
||||
6
src/client/dune
Normal file
6
src/client/dune
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
(library
|
||||
(name opentelemetry_client_ocurl)
|
||||
(public_name opentelemetry-client-ocurl)
|
||||
(libraries opentelemetry curl ocaml-protoc))
|
||||
|
||||
118
src/client/opentelemetry_client_ocurl.ml
Normal file
118
src/client/opentelemetry_client_ocurl.ml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
(*
|
||||
https://github.com/open-telemetry/oteps/blob/main/text/0035-opentelemetry-protocol.md
|
||||
https://github.com/open-telemetry/oteps/blob/main/text/0099-otlp-http.md
|
||||
*)
|
||||
|
||||
(* TODO *)
|
||||
|
||||
open Opentelemetry
|
||||
|
||||
let[@inline] (let@) f x = f x
|
||||
|
||||
let default_url = "http://localhost:4318"
|
||||
let url = ref (try Sys.getenv "OTEL_EXPORTER_OTLP_ENDPOINT" with _ -> default_url)
|
||||
let set_url s = url := s
|
||||
|
||||
let lock_ : (unit -> unit) ref = ref ignore
|
||||
let unlock_ : (unit -> unit) ref = ref ignore
|
||||
let set_mutex ~lock ~unlock : unit =
|
||||
lock_ := lock;
|
||||
unlock_ := unlock
|
||||
|
||||
let[@inline] with_lock_ f =
|
||||
!lock_();
|
||||
Fun.protect ~finally:!unlock_ f
|
||||
|
||||
let _init = lazy (
|
||||
Curl.global_init Curl.CURLINIT_GLOBALALL;
|
||||
at_exit Curl.global_cleanup;
|
||||
)
|
||||
|
||||
module Backend() : Opentelemetry.Collector.BACKEND = struct
|
||||
let() = Lazy.force _init
|
||||
|
||||
(* TODO: use Curl.Multi, etc. *)
|
||||
|
||||
let encoder = Pbrt.Encoder.create()
|
||||
let buf_res = Buffer.create 256
|
||||
|
||||
(* http client *)
|
||||
let curl : Curl.t = Curl.init ()
|
||||
|
||||
let cleanup () = Curl.cleanup curl
|
||||
|
||||
open Opentelemetry.Collector
|
||||
|
||||
(* send the content to the remote endpoint/path *)
|
||||
let send_ ~path ~decode (bod:string) : ('a, int * Status.status) result =
|
||||
Curl.reset curl;
|
||||
Curl.set_url curl (!url ^ path);
|
||||
Curl.set_httppost curl [];
|
||||
Curl.set_httpheader curl ["content-type: application/x-protobuf"];
|
||||
(* write body *)
|
||||
Curl.set_readfunction curl
|
||||
begin
|
||||
let i = ref 0 in
|
||||
(fun n ->
|
||||
let len = min n (String.length bod - !i) in
|
||||
String.sub bod !i len)
|
||||
end;
|
||||
Buffer.clear buf_res;
|
||||
Curl.set_writefunction curl
|
||||
(fun s -> Buffer.add_string buf_res s; String.length s);
|
||||
match Curl.perform curl with
|
||||
| () ->
|
||||
let code = Curl.get_responsecode curl in
|
||||
let dec = Pbrt.Decoder.of_string (Buffer.contents buf_res) in
|
||||
if code >= 200 && code < 300 then (
|
||||
let res = decode dec in
|
||||
Ok res
|
||||
) else (
|
||||
let status = Status.decode_status dec in
|
||||
Error (code, status)
|
||||
)
|
||||
| exception Curl.CurlException (_, code, msg) ->
|
||||
let status = Status.default_status
|
||||
~code:(Int32.of_int code) ~message:(Bytes.unsafe_of_string msg) () in
|
||||
Error(code, status)
|
||||
|
||||
let report_err_ code status =
|
||||
Format.eprintf "@[<2>opentelemetry: export failed with@ http code=%d@ status %a@]@."
|
||||
code Status.pp_status status
|
||||
|
||||
let send_trace (tr:Trace_service.export_trace_service_request) : unit =
|
||||
let@() = with_lock_ in
|
||||
Pbrt.Encoder.reset encoder;
|
||||
Trace_service.encode_export_trace_service_request tr encoder;
|
||||
match
|
||||
send_ ~path:"/v1/traces" ~decode:(fun _ -> ())
|
||||
(Pbrt.Encoder.to_string encoder)
|
||||
with
|
||||
| Ok () -> ()
|
||||
| Error (code, status) -> report_err_ code status
|
||||
|
||||
let send_metrics (m:Metrics_service.export_metrics_service_request) : unit =
|
||||
let@() = with_lock_ in
|
||||
Pbrt.Encoder.reset encoder;
|
||||
Metrics_service.encode_export_metrics_service_request m encoder;
|
||||
match
|
||||
send_ ~path:"/v1/metrics" ~decode:(fun _ -> ())
|
||||
(Pbrt.Encoder.to_string encoder);
|
||||
with
|
||||
| Ok () -> ()
|
||||
| Error (code, status) -> report_err_ code status
|
||||
end
|
||||
|
||||
let setup_ () =
|
||||
let module B = Backend() in
|
||||
Opentelemetry.Collector.backend := Some (module B);
|
||||
B.cleanup
|
||||
|
||||
let setup() =
|
||||
let cleanup = setup_() in
|
||||
at_exit cleanup
|
||||
|
||||
let with_setup f =
|
||||
let cleanup = setup_() in
|
||||
Fun.protect ~finally:cleanup f
|
||||
16
src/client/opentelemetry_client_ocurl.mli
Normal file
16
src/client/opentelemetry_client_ocurl.mli
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
(*
|
||||
TODO: more options from
|
||||
https://opentelemetry.io/docs/reference/specification/protocol/exporter/
|
||||
*)
|
||||
|
||||
val set_url : string -> unit
|
||||
(** Url of the endpoint. Default is "http://localhost:4318",
|
||||
or "OTEL_EXPORTER_OTLP_ENDPOINT" if set. *)
|
||||
|
||||
val set_mutex : lock:(unit -> unit) -> unlock:(unit -> unit) -> unit
|
||||
|
||||
val setup : unit -> unit
|
||||
(** Setup endpoint. This modifies {!Opentelemetry.Collector.backend}. *)
|
||||
|
||||
val with_setup : (unit -> 'a) -> 'a
|
||||
78
src/dune
Normal file
78
src/dune
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
(library
|
||||
(name opentelemetry)
|
||||
(synopsis "API for opentelemetry instrumentation")
|
||||
(flags :standard -warn-error -a+8)
|
||||
(libraries
|
||||
(select timestamp_clock.ml from
|
||||
(ptime ptime.clock.os -> timestamp_clock.ptime.ml)
|
||||
(unix -> timestamp_clock.unix.ml))
|
||||
ocaml-protoc)
|
||||
(public_name opentelemetry))
|
||||
|
||||
; ### protobuf rules ###
|
||||
|
||||
(rule
|
||||
(targets status_types.ml status_types.mli
|
||||
status_pb.ml status_pb.mli
|
||||
status_pp.ml status_pp.mli)
|
||||
(deps (:file status.proto))
|
||||
(action (run ocaml-protoc %{file} -ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets common_types.ml common_types.mli
|
||||
common_pb.ml common_pb.mli
|
||||
common_pp.ml common_pp.mli)
|
||||
(deps
|
||||
(:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/common/v1/common.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets resource_types.ml resource_types.mli
|
||||
resource_pb.ml resource_pb.mli
|
||||
resource_pp.ml resource_pp.mli)
|
||||
(deps
|
||||
(:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/resource/v1/resource.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets trace_types.ml trace_types.mli
|
||||
trace_pb.ml trace_pb.mli
|
||||
trace_pp.ml trace_pp.mli)
|
||||
(deps
|
||||
(:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/trace/v1/trace.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets metrics_types.ml metrics_types.mli
|
||||
metrics_pb.ml metrics_pb.mli
|
||||
metrics_pp.ml metrics_pp.mli)
|
||||
(deps
|
||||
(:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/metrics/v1/metrics.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets metrics_service_types.ml
|
||||
metrics_service_pp.ml metrics_service_pp.mli
|
||||
metrics_service_pb.ml metrics_service_pb.mli)
|
||||
(deps (:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/collector/metrics/v1/metrics_service.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
|
||||
(rule
|
||||
(targets trace_service_types.ml
|
||||
trace_service_pp.ml trace_service_pp.mli
|
||||
trace_service_pb.ml trace_service_pb.mli)
|
||||
(deps
|
||||
(:file %{project_root}/vendor/opentelemetry-proto/opentelemetry/proto/collector/trace/v1/trace_service.proto))
|
||||
(action (run ocaml-protoc %{file}
|
||||
-I %{project_root}/vendor/opentelemetry-proto/
|
||||
-ml_out . -pp -binary)))
|
||||
79
src/opentelemetry.ml
Normal file
79
src/opentelemetry.ml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
(** Traces.
|
||||
|
||||
See {{: https://opentelemetry.io/docs/reference/specification/overview/#tracing-signal} the spec} *)
|
||||
module Trace = struct
|
||||
include Trace_types
|
||||
include Trace_pp
|
||||
include Trace_pb
|
||||
end
|
||||
|
||||
(** Metrics.
|
||||
|
||||
See {{: https://opentelemetry.io/docs/reference/specification/overview/#metric-signal} the spec} *)
|
||||
module Metrics = struct
|
||||
include Metrics_types
|
||||
include Metrics_pp
|
||||
include Metrics_pb
|
||||
end
|
||||
|
||||
module Common = struct
|
||||
include Common_types
|
||||
include Common_pp
|
||||
include Common_pb
|
||||
end
|
||||
|
||||
|
||||
module Resource = struct
|
||||
include Resource_types
|
||||
include Resource_pp
|
||||
include Resource_pb
|
||||
end
|
||||
|
||||
(*
|
||||
module Span = Span
|
||||
module Timestamp = Timestamp
|
||||
*)
|
||||
|
||||
(** Collector types
|
||||
|
||||
These types are used by backend implementations, to send events to
|
||||
collectors such as Jaeger.
|
||||
|
||||
Note: most users will not need to touch this module *)
|
||||
module Collector = struct
|
||||
|
||||
module Trace_service = struct
|
||||
include Trace_service_types
|
||||
include Trace_service_pb
|
||||
include Trace_service_pp
|
||||
end
|
||||
|
||||
module Metrics_service = struct
|
||||
include Metrics_service_types
|
||||
include Metrics_service_pp
|
||||
include Metrics_service_pb
|
||||
end
|
||||
|
||||
module Status = struct
|
||||
include Status_types
|
||||
include Status_pp
|
||||
include Status_pb
|
||||
end
|
||||
|
||||
(** Collector client interface. *)
|
||||
module type BACKEND = sig
|
||||
|
||||
val send_trace : Trace_service.export_trace_service_request -> unit
|
||||
|
||||
val send_metrics : Metrics_service.export_metrics_service_request -> unit
|
||||
|
||||
val cleanup : unit -> unit
|
||||
end
|
||||
|
||||
type backend = (module BACKEND)
|
||||
|
||||
let backend : backend option ref = ref None
|
||||
|
||||
end
|
||||
|
||||
10
src/status.proto
Normal file
10
src/status.proto
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
syntax = "proto3";
|
||||
|
||||
// from https://pkg.go.dev/google.golang.org/genproto/googleapis/rpc/status?utm_source=godoc#Status
|
||||
|
||||
message Status {
|
||||
int32 code = 1;
|
||||
bytes message = 2;
|
||||
repeated bytes details = 3;
|
||||
}
|
||||
4
src/timestamp.ml
Normal file
4
src/timestamp.ml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
type t = float (* UTC *)
|
||||
|
||||
let now = Timestamp_clock.now
|
||||
5
src/timestamp.mli
Normal file
5
src/timestamp.mli
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
type t
|
||||
|
||||
val now : unit -> t
|
||||
|
||||
3
src/timestamp_clock.mli
Normal file
3
src/timestamp_clock.mli
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
val now : unit -> float
|
||||
(** unix time in seconds, GMT *)
|
||||
1
src/timestamp_clock.ptime.ml
Normal file
1
src/timestamp_clock.ptime.ml
Normal file
|
|
@ -0,0 +1 @@
|
|||
let now () = Ptime_clock.now () |> Ptime.to_float_s
|
||||
2
src/timestamp_clock.unix.ml
Normal file
2
src/timestamp_clock.unix.ml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
let now () = Unix.gettimeofday()
|
||||
1
vendor/opentelemetry-proto
vendored
Submodule
1
vendor/opentelemetry-proto
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit cc4ed55c082cb75e084d40b4ddf3805eda099f97
|
||||
Loading…
Add table
Reference in a new issue