├── .github ├── assets │ ├── observe-wasm-light.png │ └── observe-wasm.png └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── SDK-API-VERSIONING.md ├── demo-iota ├── Makefile ├── README.md ├── functions │ ├── count_vowels.rs │ ├── count_vowels.rs.instr.wasm │ └── count_vowels.rs.wasm ├── go │ ├── Dockerfile │ ├── go.mod │ ├── go.sum │ └── main.go ├── js │ ├── deno │ │ ├── Dockerfile │ │ ├── index.ts │ │ └── run.sh │ └── node │ │ ├── Dockerfile │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json └── rust │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Dockerfile │ └── src │ └── main.rs ├── go ├── .gitignore ├── adapter.go ├── adapter │ ├── datadog │ │ └── adapter.go │ ├── datadog_formatter │ │ └── format.go │ ├── honeycomb │ │ └── adapter.go │ ├── lightstep │ │ └── adapter.go │ ├── opentelemetry │ │ └── adapter.go │ ├── otel_stdout │ │ └── adapter.go │ └── stdout │ │ └── adapter.go ├── bin │ ├── datadog │ │ └── main.go │ ├── honeycomb │ │ └── main.go │ ├── lightstep │ │ └── main.go │ ├── opentelemetry │ │ └── main.go │ ├── otelstdout │ │ └── main.go │ └── stdout │ │ └── main.go ├── bucket.go ├── bucket_test.go ├── event.go ├── go.mod ├── go.sum ├── listener.go ├── otel_formatter.go ├── telemetry.go ├── trace_ctx.go ├── trace_ctx_test.go └── wasm.go ├── js ├── esbuild │ └── esbuild.js ├── package-lock.json ├── package.json ├── packages │ ├── observe-sdk-datadog │ │ ├── .npmignore │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── test │ │ │ ├── deno │ │ │ │ └── index.ts │ │ │ ├── node │ │ │ │ ├── index.js │ │ │ │ ├── package-lock.json │ │ │ │ └── package.json │ │ │ ├── web │ │ │ │ ├── count_vowels.instr.wasm │ │ │ │ ├── index.html │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ │ └── workerd │ │ │ │ ├── .editorconfig │ │ │ │ ├── .gitignore │ │ │ │ ├── .prettierrc │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ ├── code.wasm │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── wrangler.toml │ │ └── tsconfig.json │ ├── observe-sdk-honeycomb │ │ ├── .npmignore │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── test │ │ │ ├── deno │ │ │ │ └── index.ts │ │ │ ├── node │ │ │ │ ├── index.js │ │ │ │ ├── package-lock.json │ │ │ │ └── package.json │ │ │ ├── web │ │ │ │ ├── count_vowels.instr.wasm │ │ │ │ ├── index.html │ │ │ │ ├── index.js │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── test.c.instr.wasm │ │ │ └── workerd │ │ │ │ ├── .editorconfig │ │ │ │ ├── .gitignore │ │ │ │ ├── .prettierrc │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ ├── code.wasm │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── wrangler.toml │ │ └── tsconfig.json │ ├── observe-sdk-lightstep │ │ ├── .npmignore │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── test │ │ │ ├── deno │ │ │ │ └── index.ts │ │ │ ├── node │ │ │ │ ├── index.js │ │ │ │ ├── package-lock.json │ │ │ │ └── package.json │ │ │ ├── web │ │ │ │ ├── count_vowels.instr.wasm │ │ │ │ ├── index.html │ │ │ │ ├── index.js │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── test.c.instr.wasm │ │ │ └── workerd │ │ │ │ ├── .editorconfig │ │ │ │ ├── .gitignore │ │ │ │ ├── .prettierrc │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ ├── src │ │ │ │ ├── code.wasm │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── wrangler.toml │ │ └── tsconfig.json │ └── observe-sdk-stdout │ │ ├── .npmignore │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── test │ │ ├── deno │ │ │ └── index.ts │ │ ├── node │ │ │ ├── index.js │ │ │ ├── package-lock.json │ │ │ └── package.json │ │ ├── web │ │ │ ├── count_vowels.instr.wasm │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ └── package.json │ │ └── workerd │ │ │ ├── .editorconfig │ │ │ ├── .gitignore │ │ │ ├── .prettierrc │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── code.wasm │ │ │ └── index.ts │ │ │ ├── tsconfig.json │ │ │ └── wrangler.toml │ │ └── tsconfig.json ├── proto │ └── opentelemetry │ │ └── proto │ │ ├── common │ │ └── v1 │ │ │ └── common.ts │ │ ├── resource │ │ └── v1 │ │ │ └── resource.ts │ │ └── trace │ │ └── v1 │ │ └── trace.ts ├── src │ ├── lib │ │ ├── adapters │ │ │ ├── datadog │ │ │ │ ├── formatter.ts │ │ │ │ └── mod.ts │ │ │ ├── honeycomb │ │ │ │ └── mod.ts │ │ │ ├── lightstep │ │ │ │ └── mod.ts │ │ │ └── stdout │ │ │ │ └── mod.ts │ │ ├── collectors │ │ │ └── span │ │ │ │ ├── mod.ts │ │ │ │ └── modsurfer-demangle │ │ │ │ ├── modsurfer_demangle.d.ts │ │ │ │ ├── modsurfer_demangle.js │ │ │ │ ├── modsurfer_demangle_bg.js │ │ │ │ ├── modsurfer_demangle_bg.wasm │ │ │ │ ├── modsurfer_demangle_bg.wasm.d.ts │ │ │ │ └── package.json │ │ ├── formatters │ │ │ └── opentelemetry.ts │ │ ├── mod.ts │ │ └── parser │ │ │ └── mod.ts │ └── sdk │ │ └── mod.ts ├── test-data │ ├── rust_fib_instr.wasm │ └── test.c.instr.wasm └── tsconfig.json ├── justfile ├── observe-api ├── .clang-format ├── Makefile ├── README.md ├── c │ └── observe_api.h ├── cxx │ └── observe_api.hpp ├── go │ ├── go.mod │ └── observe_api.go ├── rust │ ├── Cargo.lock │ ├── Cargo.toml │ ├── crates │ │ └── instrument │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── lib.rs └── test │ ├── c │ └── main.c │ ├── c_guest.wasm │ ├── cxx │ ├── main.cpp │ └── main2.cpp │ ├── cxx_guest.wasm │ ├── cxx_guest_2.wasm │ ├── cxx_guest_3.wasm │ ├── rust │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs │ └── rust_guest.wasm ├── proto └── opentelemetry │ └── proto │ ├── collector │ ├── README.md │ ├── logs │ │ └── v1 │ │ │ ├── logs_service.proto │ │ │ └── logs_service_http.yaml │ ├── metrics │ │ └── v1 │ │ │ ├── metrics_service.proto │ │ │ └── metrics_service_http.yaml │ └── trace │ │ └── v1 │ │ ├── trace_service.proto │ │ └── trace_service_http.yaml │ ├── common │ └── v1 │ │ └── common.proto │ ├── logs │ └── v1 │ │ └── logs.proto │ ├── metrics │ └── v1 │ │ └── metrics.proto │ ├── resource │ └── v1 │ │ └── resource.proto │ └── trace │ └── v1 │ └── trace.proto ├── rust ├── Cargo.toml ├── build.rs ├── examples │ ├── basic.rs │ ├── datadog.rs │ ├── honeycomb.rs │ ├── lightstep.rs │ ├── many.rs │ ├── otel-stdout.rs │ ├── reactor-hello.rs │ └── zipkin.rs ├── src │ ├── adapter │ │ ├── datadog.rs │ │ ├── datadog_formatter │ │ │ └── mod.rs │ │ ├── honeycomb.rs │ │ ├── lightstep.rs │ │ ├── mod.rs │ │ ├── otel_formatter │ │ │ └── mod.rs │ │ ├── otelstdout.rs │ │ ├── stdout.rs │ │ ├── zipkin.rs │ │ └── zipkin_formatter │ │ │ └── mod.rs │ ├── collector.rs │ ├── context.rs │ ├── lib.rs │ └── wasm_instr.rs └── tests │ ├── README.md │ ├── integration_tests.rs │ ├── many_tests.rs │ ├── otel_stdout_tests.rs │ └── tests │ └── helpers │ ├── mod.rs │ └── otel_json.rs ├── scripts └── generate-otel-ts.sh └── test ├── .clang-format ├── constructor-destructor.c ├── constructor-destructor.c.instr.wasm ├── constructor-destructor.c.wasm ├── kitchensink.c ├── kitchensink.c.instr.wasm ├── kitchensink.c.wasm ├── nested.c ├── nested.c.instr.wasm ├── nested.c.wasm ├── reactor-hello.c ├── reactor-hello.c.instr.wasm ├── reactor-hello.c.wasm ├── test.c ├── test.c.instr.wasm └── test.c.wasm /.github/assets/observe-wasm-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/.github/assets/observe-wasm-light.png -------------------------------------------------------------------------------- /.github/assets/observe-wasm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/.github/assets/observe-wasm.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rust/target 2 | rust/Cargo.lock 3 | target 4 | .env 5 | build.js 6 | node_modules 7 | dist 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "rust" ] 3 | exclude = [ "observe-api/rust", "observe-api/test/rust" ] 4 | resolver = "2" 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | WASICC?=$(WASI_SDK_PATH)/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot 2 | WASM_INSTR_API_KEY?= 3 | .NOTINTERMEDIATE: 4 | .PHONY: test 5 | test: 6 | cargo run --example=basic test/test.c.instr.wasm "Test" 7 | cargo run --example=many test/test.c.instr.wasm "Test" 8 | 9 | C_MODULES := $(wildcard test/*.c) 10 | RS_MODULES := $(wildcard test/*.rs) 11 | ALL_MODULES := $(C_MODULES) $(RS_MODULES) 12 | ALL_MODULES_INSTR_WASM := $(addsuffix .instr.wasm, $(ALL_MODULES)) 13 | 14 | test/reactor-%.c.wasm: test/reactor-%.c 15 | $(WASICC) -mexec-model=reactor -o $@ $^ 16 | 17 | test/%.c.wasm: test/%.c 18 | $(WASICC) -o $@ $^ 19 | 20 | test/%.rs.wasm: test/%.rs 21 | rustc $^ --target=wasm32-wasi -C opt-level=3 -C debuginfo=0 -o $@ 22 | 23 | test/%.instr.wasm: test/%.wasm 24 | curl -F wasm=@$^ https://compiler-preview.dylibso.com/instrument -X POST -H "Authorization: Bearer $(WASM_INSTR_API_KEY)" > $@ 25 | 26 | .PHONY: instrument 27 | instrument: $(ALL_MODULES_INSTR_WASM) 28 | 29 | .PHONY: clean 30 | clean: 31 | rm -f test/*.wasm 32 | -------------------------------------------------------------------------------- /SDK-API-VERSIONING.md: -------------------------------------------------------------------------------- 1 | ## This guide is for developers of the Observe SDK. If you are just using the SDK you may ignore this document. 2 | 3 | # SDK API Versioning 4 | 5 | To avoid confusion with users of the Instrumentation Service and the Observe API, we must be careful with API changes. Users may have modules instrumented with an older version of the Instrumentation Service or built with an older version of the API. When possible we should support the older api to avoid breaking those modules. When too painful to maintain support we should at least detect the issue pre-linking and recommend a path forward. 6 | 7 | ## General How-To 8 | 1. If a change is non-breaking, such as an extension of the API maintaining the same signature, just change the function in all the sdks and call it a day. 9 | 2. Otherwise, implement the new version of the function with a new name in all the sdks. Generally, the new name would be `old-name-vX` where `X` is the version number. The first `vX` should be `v2`. 10 | 3. If possible keep the old function. It is preferred to modify it to use the new implementation when viable rather than maintaining duplicate implementations. **Pre-linking should warn that the old function is deprecated.** 11 | 4. If not possible or unwieldy, pre-linking should error out when a module contains a removed function. 12 | 13 | ## Language specific notes 14 | ### Rust 15 | Pre-linking checks are done in `rust/src/wasm_instr.rs` in `WasmInstrInfo::new` 16 | ### Go 17 | Pre-linking checks are done in `go/wasm.go` in `parseNames` 18 | ### JS 19 | Pre-linking checks are done in `js/src/lib/collectors/span/mod.ts` in `SpanCollector::setNames` 20 | 21 | ## Observe API SDKs 22 | TBD whether to change the signatures of the prototypes to match the new versions or to add new prototypes for the new versions. 23 | 24 | ## Wasm-Instr / Instrumentation Service 25 | Bump `WASM_INSTR_VERSION_MAJOR` and `WASM_INSTR_VERSION_MINOR` (and equivalents in Go and JS) when the observe sdk should warn when a module older than those is loaded (Such as when older versions use deprecated or removed functions). This is purely advisory information, the other SDK API Versioning checks handle whether the observe sdk will error out. 26 | 27 | -------------------------------------------------------------------------------- /demo-iota/Makefile: -------------------------------------------------------------------------------- 1 | WASM_INSTR_API_KEY=? 2 | STDIN_FILE := /tmp/makefile_tmp 3 | RS_MODULES := $(wildcard functions/*.rs) 4 | 5 | name="" 6 | wasm="" 7 | upload: 8 | @curl -F wasm=@$(wasm) "https://p01--iota-web--tyqfmnr79gjf.code.run/upload?name=$(name)" -X POST 9 | 10 | run: 11 | @cat > $(STDIN_FILE) 12 | @curl "https://p01--iota-web--tyqfmnr79gjf.code.run/run?name=$(name)" -X POST -d "$$(cat $(STDIN_FILE))" 13 | @rm $(STDIN_FILE) 14 | 15 | build: 16 | @for file in $(RS_MODULES); do \ 17 | rustc $$file --target=wasm32-wasi -C opt-level=3 -C debuginfo=0 -o $$file.wasm; \ 18 | curl -F wasm=@$$file.wasm https://compiler-preview.dylibso.com/instrument -X POST -H "Authorization: Bearer $(WASM_INSTR_API_KEY)" > $$file.instr.wasm; \ 19 | done 20 | 21 | iota-upload: 22 | @curl -F wasm=@$(wasm) "https://iota.dylibso.com/upload/$(host)/$(name)" -X POST 23 | 24 | iota-run: 25 | @cat > $(STDIN_FILE) 26 | @curl "https://iota.dylibso.com/run/$(host)/$(adapter)/$(name)" -X POST -d "$$(cat $(STDIN_FILE))" 27 | @rm $(STDIN_FILE) 28 | -------------------------------------------------------------------------------- /demo-iota/functions/count_vowels.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | const VOWELS: &[char] = &['a', 'A', 'e', 'E', 'i', 'I', 'o', 'O', 'u', 'U']; 4 | 5 | fn main() -> io::Result<()> { 6 | let mut buffer = String::new(); 7 | io::stdin().read_line(&mut buffer)?; 8 | let mut count = 0; 9 | for ch in buffer.chars() { 10 | if VOWELS.contains(&ch) { 11 | count += 1; 12 | } 13 | } 14 | 15 | println!("{}", count); 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /demo-iota/functions/count_vowels.rs.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/demo-iota/functions/count_vowels.rs.instr.wasm -------------------------------------------------------------------------------- /demo-iota/functions/count_vowels.rs.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/demo-iota/functions/count_vowels.rs.wasm -------------------------------------------------------------------------------- /demo-iota/go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 2 | 3 | ADD /go /app/go 4 | ADD /demo-iota/go /app/demo-iota/go 5 | 6 | WORKDIR /app/demo-iota/go/ 7 | RUN go mod tidy 8 | 9 | RUN go build -o /iota 10 | CMD ["/iota"] 11 | 12 | -------------------------------------------------------------------------------- /demo-iota/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dylibso/observe-sdk/demo-iota/go 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/dylibso/observe-sdk/go v0.0.0-20230817180337-d820d9baa3bb 7 | github.com/tetratelabs/wazero v1.4.0 8 | ) 9 | 10 | replace github.com/dylibso/observe-sdk/go => ../../go 11 | 12 | require ( 13 | github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab // indirect 14 | github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect 15 | go.opentelemetry.io/proto/otlp v1.0.0 // indirect 16 | google.golang.org/protobuf v1.31.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /demo-iota/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/dylibso/observe-sdk/go v0.0.0-20230627012040-a33ef418d181 h1:4VtTf5Qn4jnHEDI7bA71yueVKrkOjWk+JilJwmawUxk= 3 | github.com/dylibso/observe-sdk/go v0.0.0-20230627012040-a33ef418d181/go.mod h1:q7rVZhjfqpjzOX5YCIoZa+hExCRl5cYeQKaxTVNIKfg= 4 | github.com/dylibso/observe-sdk/go v0.0.0-20230627212343-0f334d69137f h1:NPjIcBJIDLn6Mjt5gxfE72UcglYiXPRDpYJC9dA0414= 5 | github.com/dylibso/observe-sdk/go v0.0.0-20230627212343-0f334d69137f/go.mod h1:q7rVZhjfqpjzOX5YCIoZa+hExCRl5cYeQKaxTVNIKfg= 6 | github.com/dylibso/observe-sdk/go v0.0.0-20230627221207-8daea0862c34 h1:z4+ykeHQ/BYMO5oQ+TnsOX6IvTa2w14Jw++FCUlMbkA= 7 | github.com/dylibso/observe-sdk/go v0.0.0-20230627221207-8daea0862c34/go.mod h1:q7rVZhjfqpjzOX5YCIoZa+hExCRl5cYeQKaxTVNIKfg= 8 | github.com/dylibso/observe-sdk/go v0.0.0-20230817180337-d820d9baa3bb h1:S8v3XYq3KGuPg+0ZWzhUEHVXYoNj51Hg6D8WGxywp9c= 9 | github.com/dylibso/observe-sdk/go v0.0.0-20230817180337-d820d9baa3bb/go.mod h1:I4k2W7iEaZ1709ddV48M9FsKcZTal1I0+EKoiJgaIsw= 10 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 11 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 12 | github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab h1:BA4a7pe6ZTd9F8kXETBoijjFJ/ntaa//1wiH9BZu4zU= 13 | github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 16 | github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 h1:ZF+QBjOI+tILZjBaFj3HgFonKXUcwgJ4djLb6i42S3Q= 17 | github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834/go.mod h1:m9ymHTgNSEjuxvw8E7WWe4Pl4hZQHXONY8wE6dMLaRk= 18 | github.com/tetratelabs/wazero v1.2.1 h1:J4X2hrGzJvt+wqltuvcSjHQ7ujQxA9gb6PeMs4qlUWs= 19 | github.com/tetratelabs/wazero v1.2.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= 20 | github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk= 21 | github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= 22 | go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= 23 | go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= 24 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 25 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 26 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 27 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 28 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 29 | -------------------------------------------------------------------------------- /demo-iota/js/deno/Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM denoland/deno:ubuntu-1.36.1 2 | FROM node:20.4.0 3 | 4 | EXPOSE 3000 5 | 6 | # install curl, so we can install nvm, so we can install node 7 | ENV PATH="${PATH}:/usr/bin" 8 | RUN apt update 9 | RUN apt install -y curl 10 | RUN curl -fsSL https://deno.land/x/install/install.sh | sh 11 | RUN echo $PATH 12 | ADD /js /js 13 | ADD /demo-iota/js/deno /demo-iota/js/deno 14 | 15 | # install the global build dependencies used for each adapter 16 | WORKDIR /js 17 | RUN npm i 18 | 19 | # build the packages for each adapter 20 | WORKDIR /js/packages/observe-sdk-datadog 21 | RUN npm run build 22 | 23 | WORKDIR /js/packages/observe-sdk-honeycomb 24 | RUN npm run build 25 | 26 | WORKDIR /js/packages/observe-sdk-lightstep 27 | RUN npm run build 28 | 29 | WORKDIR /js/packages/observe-sdk-stdout 30 | RUN npm run build 31 | 32 | WORKDIR /demo-iota/js/deno 33 | 34 | CMD ./run.sh 35 | -------------------------------------------------------------------------------- /demo-iota/js/deno/run.sh: -------------------------------------------------------------------------------- 1 | $HOME/.deno/bin/deno run --allow-net --allow-write --allow-env --allow-read index.ts 2 | 3 | # Wait for any process to exit 4 | wait -n 5 | 6 | # Exit with status of process that exited first 7 | exit $? -------------------------------------------------------------------------------- /demo-iota/js/node/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20.4.0 2 | 3 | EXPOSE 3000 4 | 5 | ADD /js /js 6 | ADD /demo-iota/js/node /demo-iota/js/node 7 | 8 | # install the global build dependencies used for each adapter 9 | WORKDIR /js 10 | RUN npm i 11 | 12 | # build the packages for each adapter 13 | WORKDIR /js/packages/observe-sdk-datadog 14 | RUN npm run build 15 | 16 | WORKDIR /js/packages/observe-sdk-honeycomb 17 | RUN npm run build 18 | 19 | WORKDIR /js/packages/observe-sdk-lightstep 20 | RUN npm run build 21 | 22 | WORKDIR /js/packages/observe-sdk-stdout 23 | RUN npm run build 24 | 25 | WORKDIR /demo-iota/js/node 26 | RUN npm i 27 | 28 | CMD node index.js 29 | -------------------------------------------------------------------------------- /demo-iota/js/node/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const os = require('os') 3 | const multer = require('multer') 4 | const fs = require('fs'); 5 | const { WASI } = require('wasi'); 6 | const { env } = require('node:process'); 7 | const { DatadogAdapter } = require('../../../js/packages/observe-sdk-datadog'); 8 | const bodyParser = require('body-parser') 9 | 10 | const storage = multer.diskStorage( 11 | { 12 | destination: os.tmpdir(), 13 | filename: (req, _, cb) => { 14 | cb(null, `${req.query['name']}.wasm`); 15 | } 16 | } 17 | ) 18 | const upload = multer({ storage }) 19 | const app = express() 20 | 21 | const config = { 22 | agentHost: new URL("http://ddagent:8126"), 23 | serviceName: "iota-node", 24 | defaultTags: new Map(), 25 | traceType: "node", 26 | emitTracesInterval: 1000, // milliseconds 27 | traceBatchMax: 100 28 | } 29 | const adapter = new DatadogAdapter(config) 30 | 31 | app.get('/', (_, res) => { 32 | res.send('Hi') 33 | }) 34 | 35 | app.post('/upload', upload.single('wasm'), (req, res) => { 36 | try { 37 | const _ = fs.readFileSync(`${os.tmpdir()}/${req.query['name']}.wasm`) 38 | console.log(`Successfully uploaded ${req.query['name']}.wasm`) 39 | res.status(200) 40 | res.send() 41 | } catch (e) { 42 | console.error(e) 43 | res.sendStatus(500) 44 | } 45 | }) 46 | 47 | app.post('/run', bodyParser.raw({ limit: '256mb', type: () => true }), async (req, res) => { 48 | try { 49 | const stdoutPath = `${os.tmpdir}/stdout_${Math.ceil(Math.random() * 10000)}.txt` 50 | const stdout = fs.openSync(stdoutPath, 'w') 51 | const stdinPath = `${os.tmpdir}/stdin_${Math.ceil(Math.random() * 10000)}.txt` 52 | fs.writeFileSync(stdinPath, req.body) 53 | const stdin = fs.openSync(stdinPath) 54 | 55 | const wasi = new WASI({ 56 | version: 'preview1', 57 | args: [req.query['name']], 58 | stdin, 59 | stdout, 60 | env, 61 | }) 62 | 63 | // NOTE: The wasm code loaded here will only report any metrics via the adapter _if the code is instrumented_. 64 | // If you expect to see telemetry data, please be sure you're running instrumented code. 65 | // This section of the docs is a good place to start: 66 | // https://dev.dylibso.com/docs/observe/overview#2-instrumenting-your-code-automatic-or-manual 67 | const bytes = fs.readFileSync(`${os.tmpdir()}/${req.query['name']}.wasm`) 68 | const traceContext = await adapter.start(bytes) 69 | const module = new WebAssembly.Module(bytes) 70 | const instance = await WebAssembly.instantiate(module, { 71 | ...wasi.getImportObject(), 72 | ...traceContext.getImportObject(), 73 | }) 74 | wasi.start(instance) 75 | traceContext.setMetadata({ 76 | http_status_code: '200', 77 | http_url: `${req.protocol}://${req.headers['host']}${req.originalUrl}`, 78 | }) 79 | traceContext.stop() 80 | res.status(200) 81 | res.send(fs.readFileSync(stdoutPath)) 82 | fs.unlinkSync(stdoutPath) 83 | fs.unlinkSync(stdinPath) 84 | } catch (e) { 85 | console.error(e) 86 | res.sendStatus(500) 87 | } 88 | }) 89 | 90 | const port = 3000 91 | app.listen(port, () => { 92 | console.log(`Listening on port ${port}`) 93 | }) 94 | -------------------------------------------------------------------------------- /demo-iota/js/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "body-parser": "^1.20.2", 4 | "express": "^4.18.2", 5 | "multer": "^1.4.5-lts.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo-iota/rust/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /demo-iota/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iota" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | 7 | [workspace] 8 | 9 | 10 | [dependencies] 11 | anyhow = "1.0.71" 12 | axum = { version = "0.6.18", features = ["multipart"] } 13 | serde = "1.0.164" 14 | tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] } 15 | wasi-common = ">= 17.0.0, < 18.0.0" 16 | wasmtime = ">= 17.0.0, < 18.0.0" 17 | wasmtime-wasi = ">= 17.0.0, < 18.0.0" 18 | dylibso-observe-sdk = { path = "../../rust" } 19 | futures-util = "0.3.28" 20 | -------------------------------------------------------------------------------- /demo-iota/rust/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | 3 | RUN apt update -y && apt install libssl-dev pkg-config openssl curl build-essential protobuf-compiler -y 4 | 5 | RUN curl https://sh.rustup.rs -sSf | bash -s -- -y 6 | ENV PATH="/root/.cargo/bin:${PATH}" 7 | RUN rustup toolchain add stable 8 | 9 | ADD /rust /app/rust 10 | ADD /proto /app/proto 11 | ADD /demo-iota/rust /app/demo-iota/rust 12 | 13 | WORKDIR /app/demo-iota/rust 14 | RUN cargo install --path . 15 | 16 | CMD ["iota"] 17 | 18 | -------------------------------------------------------------------------------- /go/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .env -------------------------------------------------------------------------------- /go/adapter/datadog_formatter/format.go: -------------------------------------------------------------------------------- 1 | package datadog_formatter 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | 8 | observe "github.com/dylibso/observe-sdk/go" 9 | ) 10 | 11 | type DatadogFormatter []Trace 12 | 13 | type Trace []*Span 14 | 15 | type Span struct { 16 | TraceId uint64 `json:"trace_id"` 17 | SpanId uint64 `json:"span_id"` 18 | ParentId *uint64 `json:"parent_id,omitempty"` 19 | Name string `json:"name"` 20 | Start uint64 `json:"start"` 21 | Duration uint64 `json:"duration"` 22 | Resource string `json:"resource"` 23 | Error uint8 `json:"error"` 24 | Meta map[string]string `json:"meta"` 25 | Metrics map[string]string `json:"metrics"` 26 | Service string `json:"service"` 27 | Type *string `json:"type,omitempty"` 28 | } 29 | 30 | func NewSpan(service string, traceId uint64, parentId *uint64, name string, start, end time.Time) *Span { 31 | id := observe.NewSpanId() 32 | span := Span{ 33 | SpanId: id.ToUint64(), 34 | ParentId: parentId, 35 | TraceId: traceId, 36 | Name: name, 37 | Start: uint64(start.UnixNano()), 38 | Duration: uint64(end.Sub(start).Nanoseconds()), 39 | Service: service, 40 | Resource: name, 41 | } 42 | return &span 43 | } 44 | 45 | func (s *Span) AddAllocation(amount uint32) { 46 | if s.Meta == nil { 47 | s.Meta = make(map[string]string) 48 | } 49 | 50 | existingAmount, err := strconv.Atoi(s.Meta["allocation"]) 51 | if err == nil && existingAmount > 0 { 52 | s.Meta["allocation"] = fmt.Sprintf("%d", amount+uint32(existingAmount)) 53 | } else { 54 | s.Meta["allocation"] = fmt.Sprintf("%d", amount) 55 | } 56 | } 57 | 58 | func (s *Span) AddTag(key, value string) { 59 | s.Meta[key] = value 60 | } 61 | 62 | func New() *DatadogFormatter { 63 | return &DatadogFormatter{} 64 | } 65 | 66 | func (d *DatadogFormatter) AddTrace(trace Trace) { 67 | *d = append(*d, trace) 68 | } 69 | 70 | type DatadogTraceType int 71 | 72 | const ( 73 | Web DatadogTraceType = iota 74 | Db 75 | Cache 76 | Custom 77 | ) 78 | 79 | func (d DatadogTraceType) String() string { 80 | switch d { 81 | case Web: 82 | return "web" 83 | case Db: 84 | return "db" 85 | case Cache: 86 | return "cache" 87 | case Custom: 88 | return "custom" 89 | default: 90 | return "unknown-trace-type" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /go/adapter/otel_stdout/adapter.go: -------------------------------------------------------------------------------- 1 | package otel_stdout 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | 9 | observe "github.com/dylibso/observe-sdk/go" 10 | trace "go.opentelemetry.io/proto/otlp/trace/v1" 11 | ) 12 | 13 | type OtelStdoutAdapter struct { 14 | *observe.AdapterBase 15 | } 16 | 17 | func NewOtelStdoutAdapter() *OtelStdoutAdapter { 18 | base := observe.NewAdapterBase(1, 0) 19 | adapter := &OtelStdoutAdapter{ 20 | AdapterBase: &base, 21 | } 22 | 23 | adapter.AdapterBase.SetFlusher(adapter) 24 | 25 | return adapter 26 | } 27 | 28 | func (o *OtelStdoutAdapter) HandleTraceEvent(te observe.TraceEvent) { 29 | o.AdapterBase.HandleTraceEvent(te) 30 | } 31 | 32 | func (o *OtelStdoutAdapter) Flush(evts []observe.TraceEvent) error { 33 | for _, te := range evts { 34 | traceId := te.TelemetryId.ToHex16() 35 | 36 | var allSpans []*trace.Span 37 | for _, e := range te.Events { 38 | switch event := e.(type) { 39 | case observe.CallEvent: 40 | spans := o.MakeOtelCallSpans(event, nil, traceId) 41 | if len(spans) > 0 { 42 | allSpans = append(allSpans, spans...) 43 | } 44 | case observe.MemoryGrowEvent: 45 | log.Println("MemoryGrowEvent should be attached to a span") 46 | case observe.CustomEvent: 47 | log.Println("Otel adapter does not respect custom events") 48 | case observe.MetricEvent: 49 | log.Printf("metric: %s\n", event.Message) 50 | case observe.LogEvent: 51 | log.Println(event.Message) 52 | } 53 | } 54 | 55 | if len(allSpans) == 0 { 56 | return nil 57 | } 58 | 59 | t := observe.NewOtelTrace(traceId, "golang", allSpans) 60 | b, err := json.Marshal(t.TracesData) 61 | if err != nil { 62 | log.Println("failed to encode CallEvent spans") 63 | return nil 64 | } 65 | 66 | fmt.Println(string(b)) 67 | } 68 | 69 | return nil 70 | } 71 | 72 | func (o *OtelStdoutAdapter) Start(ctx context.Context) { 73 | o.AdapterBase.Start(ctx, o) 74 | } 75 | -------------------------------------------------------------------------------- /go/adapter/stdout/adapter.go: -------------------------------------------------------------------------------- 1 | package stdout 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "strings" 7 | 8 | observe "github.com/dylibso/observe-sdk/go" 9 | ) 10 | 11 | type StdoutAdapter struct { 12 | *observe.AdapterBase 13 | } 14 | 15 | func NewStdoutAdapter() *StdoutAdapter { 16 | base := observe.NewAdapterBase(1, 0) 17 | adapter := &StdoutAdapter{ 18 | AdapterBase: &base, 19 | } 20 | 21 | adapter.AdapterBase.SetFlusher(adapter) 22 | 23 | return adapter 24 | } 25 | 26 | func (s *StdoutAdapter) HandleTraceEvent(te observe.TraceEvent) { 27 | s.AdapterBase.HandleTraceEvent(te) 28 | } 29 | 30 | func (s *StdoutAdapter) Flush(evts []observe.TraceEvent) error { 31 | for _, te := range evts { 32 | for _, e := range te.Events { 33 | switch event := e.(type) { 34 | case observe.CallEvent: 35 | s.printEvents(event, 0) 36 | case observe.MemoryGrowEvent: 37 | name := event.FunctionName() 38 | log.Println("Allocated", event.MemoryGrowAmount(), "pages of memory in", name) 39 | case observe.CustomEvent: 40 | log.Println(event.Name, event.Time) 41 | case observe.MetricEvent: 42 | log.Printf("metric: %s\n", event.Message) 43 | case observe.LogEvent: 44 | log.Println(event.Message) 45 | } 46 | } 47 | } 48 | 49 | return nil 50 | } 51 | 52 | func (s *StdoutAdapter) printEvents(event observe.CallEvent, indentation int) { 53 | name := event.FunctionName() 54 | log.Println(strings.Repeat(" ", indentation), "Call to", name, "took", event.Duration) 55 | for _, event := range event.Within() { 56 | if call, ok := event.(observe.CallEvent); ok { 57 | s.printEvents(call, indentation+1) 58 | } else if alloc, ok := event.(observe.MemoryGrowEvent); ok { 59 | log.Println(strings.Repeat(" ", indentation), " - Allocated", alloc.MemoryGrowAmount(), "pages of memory in", name) 60 | } else if spanTags, ok := event.(observe.SpanTagsEvent); ok { 61 | log.Println(strings.Repeat(" ", indentation), " - Span tags:", spanTags.Tags) 62 | } else if metric, ok := event.(observe.MetricEvent); ok { 63 | log.Println(strings.Repeat(" ", indentation), " - Metric:", metric.Message) 64 | } else if logEvent, ok := event.(observe.LogEvent); ok { 65 | log.Println(strings.Repeat(" ", indentation), " - Log:", logEvent.Message) 66 | } 67 | } 68 | } 69 | 70 | func (s *StdoutAdapter) Start(ctx context.Context) { 71 | s.AdapterBase.Start(ctx, s) 72 | } 73 | -------------------------------------------------------------------------------- /go/bin/datadog/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dylibso/observe-sdk/go/adapter/datadog" 10 | "github.com/tetratelabs/wazero" 11 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 12 | ) 13 | 14 | func main() { 15 | ctx := context.Background() 16 | 17 | // we only need to create and start once per instance of our host app 18 | ddconf := datadog.DefaultDatadogConfig() 19 | adapter := datadog.NewDatadogAdapter(ddconf) 20 | 21 | defer adapter.Stop(true) 22 | adapter.Start(ctx) 23 | 24 | // Load WASM from disk 25 | wasm, err := os.ReadFile(os.Args[1]) 26 | if err != nil { 27 | log.Panicln(err) 28 | } 29 | 30 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 31 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 32 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) 33 | if err != nil { 34 | log.Panicln(err) 35 | } 36 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 37 | 38 | config := wazero.NewModuleConfig(). 39 | WithStdin(os.Stdin). 40 | WithStdout(os.Stdout). 41 | WithStderr(os.Stderr). 42 | WithArgs(os.Args[1:]...). 43 | WithStartFunctions("_start") 44 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 45 | if err != nil { 46 | log.Panicln(err) 47 | } 48 | defer m.Close(ctx) 49 | 50 | // normally this metadata would be in your web-server framework 51 | // or derived when you need them. we're just gonna initialize 52 | // some example values here 53 | resourceName := "my-resource" 54 | httpUrl := "https://example.com/my-endpoint" 55 | httpStatusCode := 200 56 | spanKind := datadog.Server 57 | clientIp := "66.210.227.34" 58 | 59 | meta := datadog.DatadogMetadata{ 60 | ResourceName: &resourceName, 61 | HttpUrl: &httpUrl, 62 | HttpStatusCode: &httpStatusCode, 63 | SpanKind: &spanKind, 64 | HttpClientIp: &clientIp, 65 | } 66 | traceCtx.Metadata(meta) 67 | 68 | traceCtx.Finish() 69 | time.Sleep(2 * time.Second) 70 | } 71 | -------------------------------------------------------------------------------- /go/bin/honeycomb/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dylibso/observe-sdk/go/adapter/honeycomb" 10 | "github.com/tetratelabs/wazero" 11 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 12 | ) 13 | 14 | func main() { 15 | ctx := context.Background() 16 | 17 | // we only need to create and start once per instance of our host app 18 | conf := &honeycomb.HoneycombConfig{ 19 | ApiKey: os.Getenv("HONEYCOMB_API_KEY"), 20 | Dataset: "golang", 21 | EmitTracesInterval: time.Second * 1, 22 | TraceBatchMax: 100, 23 | Host: "https://api.honeycomb.io", 24 | } 25 | adapter := honeycomb.NewHoneycombAdapter(conf) 26 | defer adapter.Stop(true) 27 | adapter.Start(ctx) 28 | 29 | // Load WASM from disk 30 | wasm, err := os.ReadFile(os.Args[1]) 31 | if err != nil { 32 | log.Panicln(err) 33 | } 34 | 35 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 36 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 37 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) 38 | if err != nil { 39 | log.Panicln(err) 40 | } 41 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 42 | 43 | config := wazero.NewModuleConfig(). 44 | WithStdin(os.Stdin). 45 | WithStdout(os.Stdout). 46 | WithStderr(os.Stderr). 47 | WithArgs(os.Args[1:]...). 48 | WithStartFunctions("_start") 49 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 50 | if err != nil { 51 | log.Panicln(err) 52 | } 53 | defer m.Close(ctx) 54 | 55 | // normally this metadata would be in your web-server framework 56 | // or derived when you need them 57 | 58 | meta := map[string]string{ 59 | "http.url": "https://example.com/my-endpoint", 60 | "http.status_code": "200", 61 | "http.client_ip": "66.210.227.34", 62 | } 63 | traceCtx.Metadata(meta) 64 | 65 | traceCtx.Finish() 66 | time.Sleep(2 * time.Second) 67 | } 68 | -------------------------------------------------------------------------------- /go/bin/lightstep/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dylibso/observe-sdk/go/adapter/lightstep" 10 | 11 | "github.com/tetratelabs/wazero" 12 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 13 | ) 14 | 15 | func main() { 16 | ctx := context.Background() 17 | 18 | // we only need to create and start once per instance of our host app 19 | conf := &lightstep.LightstepConfig{ 20 | ApiKey: os.Getenv("LIGHTSTEP_API_KEY"), 21 | ServiceName: "golang", 22 | EmitTracesInterval: time.Second * 1, 23 | TraceBatchMax: 100, 24 | Host: "https://ingest.lightstep.com", 25 | } 26 | adapter := lightstep.NewLightstepAdapter(conf) 27 | defer adapter.Stop(true) 28 | adapter.Start(ctx) 29 | 30 | // Load WASM from disk 31 | wasm, err := os.ReadFile(os.Args[1]) 32 | if err != nil { 33 | log.Panicln(err) 34 | } 35 | 36 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 37 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 38 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) 39 | if err != nil { 40 | log.Panicln(err) 41 | } 42 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 43 | 44 | config := wazero.NewModuleConfig(). 45 | WithStdin(os.Stdin). 46 | WithStdout(os.Stdout). 47 | WithStderr(os.Stderr). 48 | WithArgs(os.Args[1:]...). 49 | WithStartFunctions("_start") 50 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 51 | if err != nil { 52 | log.Panicln(err) 53 | } 54 | defer m.Close(ctx) 55 | 56 | // normally this metadata would be in your web-server framework 57 | // or derived when you need them 58 | 59 | meta := map[string]string{ 60 | "http.url": "https://example.com/my-endpoint", 61 | "http.status_code": "200", 62 | "http.client_ip": "66.210.227.34", 63 | } 64 | traceCtx.Metadata(meta) 65 | 66 | traceCtx.Finish() 67 | time.Sleep(2 * time.Second) 68 | } 69 | -------------------------------------------------------------------------------- /go/bin/opentelemetry/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dylibso/observe-sdk/go/adapter/opentelemetry" 10 | 11 | "github.com/tetratelabs/wazero" 12 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 13 | ) 14 | 15 | func main() { 16 | ctx := context.Background() 17 | 18 | // we only need to create and start once per instance of our host app 19 | conf := &opentelemetry.OTelConfig{ 20 | ServiceName: "golang", 21 | EmitTracesInterval: time.Second * 1, 22 | TraceBatchMax: 100, 23 | Endpoint: "localhost:4317", 24 | Protocol: opentelemetry.GRPC, 25 | AllowInsecure: true, // for localhost in dev via http 26 | } 27 | adapter := opentelemetry.NewOTelAdapter(conf) 28 | defer adapter.StopWithContext(ctx, true) 29 | adapter.Start(ctx) 30 | 31 | // Load WASM from disk 32 | wasm, err := os.ReadFile(os.Args[1]) 33 | if err != nil { 34 | log.Panicln(err) 35 | } 36 | 37 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 38 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 39 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, nil) 40 | if err != nil { 41 | log.Panicln(err) 42 | } 43 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 44 | 45 | config := wazero.NewModuleConfig(). 46 | WithStdin(os.Stdin). 47 | WithStdout(os.Stdout). 48 | WithStderr(os.Stderr). 49 | WithArgs(os.Args[1:]...). 50 | WithStartFunctions("_start") 51 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 52 | if err != nil { 53 | log.Panicln(err) 54 | } 55 | defer m.Close(ctx) 56 | 57 | // normally this metadata would be in your web-server framework 58 | // or derived when you need them 59 | meta := map[string]string{ 60 | "http.url": "https://example.com/my-endpoint", 61 | "http.status_code": "200", 62 | "http.client_ip": "66.210.227.34", 63 | } 64 | traceCtx.Metadata(meta) 65 | 66 | traceCtx.Finish() 67 | time.Sleep(2 * time.Second) 68 | } 69 | -------------------------------------------------------------------------------- /go/bin/otelstdout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | observe "github.com/dylibso/observe-sdk/go" 10 | "github.com/dylibso/observe-sdk/go/adapter/otel_stdout" 11 | "github.com/tetratelabs/wazero" 12 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 13 | ) 14 | 15 | func main() { 16 | ctx := context.Background() 17 | 18 | // we only need to create and start once per instance of our host app 19 | adapter := otel_stdout.NewOtelStdoutAdapter() 20 | defer adapter.Stop(true) 21 | adapter.Start(ctx) 22 | 23 | // Load WASM from disk 24 | wasm, err := os.ReadFile(os.Args[1]) 25 | if err != nil { 26 | log.Panicln(err) 27 | } 28 | 29 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 30 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 31 | opts := observe.Options{ 32 | ChannelBufferSize: 1024, 33 | SpanFilter: &observe.SpanFilter{ 34 | MinDuration: 0, 35 | }, 36 | } 37 | 38 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, &opts) 39 | if err != nil { 40 | log.Panicln(err) 41 | } 42 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 43 | 44 | config := wazero.NewModuleConfig(). 45 | WithStdin(os.Stdin). 46 | WithStdout(os.Stdout). 47 | WithStderr(os.Stderr). 48 | WithArgs(os.Args[1:]...). 49 | WithStartFunctions("_start") 50 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 51 | if err != nil { 52 | log.Panicln(err) 53 | } 54 | defer m.Close(ctx) 55 | 56 | traceCtx.Finish() 57 | time.Sleep(2 * time.Second) 58 | } 59 | -------------------------------------------------------------------------------- /go/bin/stdout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | observe "github.com/dylibso/observe-sdk/go" 10 | "github.com/dylibso/observe-sdk/go/adapter/stdout" 11 | "github.com/tetratelabs/wazero" 12 | "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 13 | ) 14 | 15 | func main() { 16 | ctx := context.Background() 17 | 18 | // we only need to create and start once per instance of our host app 19 | adapter := stdout.NewStdoutAdapter() 20 | defer adapter.Stop(true) 21 | adapter.Start(ctx) 22 | 23 | // Load WASM from disk 24 | wasm, err := os.ReadFile(os.Args[1]) 25 | if err != nil { 26 | log.Panicln(err) 27 | } 28 | 29 | cfg := wazero.NewRuntimeConfig().WithCustomSections(true) 30 | rt := wazero.NewRuntimeWithConfig(ctx, cfg) 31 | 32 | opts := observe.Options{ 33 | ChannelBufferSize: 1024, 34 | SpanFilter: &observe.SpanFilter{ 35 | MinDuration: 0, 36 | }, 37 | } 38 | 39 | traceCtx, err := adapter.NewTraceCtx(ctx, rt, wasm, &opts) 40 | if err != nil { 41 | log.Panicln(err) 42 | } 43 | wasi_snapshot_preview1.MustInstantiate(ctx, rt) 44 | 45 | config := wazero.NewModuleConfig(). 46 | WithStdin(os.Stdin). 47 | WithStdout(os.Stdout). 48 | WithStderr(os.Stderr). 49 | WithArgs(os.Args[1:]...). 50 | WithStartFunctions("_start") 51 | m, err := rt.InstantiateWithConfig(ctx, wasm, config) 52 | if err != nil { 53 | log.Panicln(err) 54 | } 55 | defer m.Close(ctx) 56 | 57 | traceCtx.Finish() 58 | time.Sleep(2 * time.Second) 59 | } 60 | -------------------------------------------------------------------------------- /go/bucket.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Flusher interface { 10 | Flush(events []TraceEvent) error 11 | } 12 | 13 | // EventBucket is a bucket for outgoing TraceEvents. 14 | // It only schedules flushes when the bucket goes from empty to 1 item. 15 | // At most the latency to flush the bucket will be flushPeriod. 16 | // It will also flush the TraceEvents in batches according to batch size 17 | type EventBucket struct { 18 | mu sync.Mutex 19 | wg sync.WaitGroup 20 | bucket []TraceEvent 21 | flushPeriod time.Duration 22 | batchSize int 23 | } 24 | 25 | // NewEventBucket creates an EventBucket 26 | func NewEventBucket(batchSize int, flushPeriod time.Duration) *EventBucket { 27 | return &EventBucket{ 28 | flushPeriod: flushPeriod, 29 | batchSize: batchSize, 30 | } 31 | } 32 | 33 | // addEvent adds a TraceEvent and schedules to flush to Flusher if needed 34 | func (b *EventBucket) addEvent(e TraceEvent, f Flusher) { 35 | b.mu.Lock() 36 | wasEmpty := len(b.bucket) == 0 37 | b.bucket = append(b.bucket, e) 38 | b.mu.Unlock() 39 | // if this is the first event in the bucket, 40 | // we schedule a flush 41 | if wasEmpty { 42 | b.scheduleFlush(f) 43 | } 44 | } 45 | 46 | // Wait will block until all pending flushes are done 47 | func (b *EventBucket) Wait() { 48 | b.wg.Wait() 49 | } 50 | 51 | // scheduleFlush schedules a goroutine to flush 52 | // the bucket at some time in the future depending on flushPeriod. 53 | // Events will continue to build up until the flush comes due 54 | func (b *EventBucket) scheduleFlush(f Flusher) { 55 | // we start this routine and immediately wait, we are effectively 56 | // scheduling the flush to run flushPeriod sections later. In the meantime, 57 | // events may still be coming into the eventBucket 58 | go func() { 59 | // register this flush with the wait group 60 | defer b.wg.Done() 61 | b.wg.Add(1) 62 | 63 | // wait for flushPeriod 64 | time.Sleep(b.flushPeriod) 65 | 66 | // move the events out of the EventBucket to a slice 67 | // and add 1 to the waitgroup 68 | b.mu.Lock() 69 | bucket := b.bucket 70 | b.bucket = nil 71 | b.mu.Unlock() 72 | 73 | // flush the bucket in chunks of batchSize 74 | for i := 0; i < len(bucket); i += b.batchSize { 75 | j := i + b.batchSize 76 | if j > len(bucket) { 77 | j = len(bucket) 78 | } 79 | // TODO retry logic? 80 | err := f.Flush(bucket[i:j]) 81 | if err != nil { 82 | log.Println(err) 83 | } 84 | } 85 | }() 86 | } 87 | -------------------------------------------------------------------------------- /go/bucket_test.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | type TestFlusher struct { 9 | NumFlushes int 10 | FlushedEvents [][]TraceEvent 11 | } 12 | 13 | func (f *TestFlusher) Flush(events []TraceEvent) error { 14 | f.NumFlushes += 1 15 | f.FlushedEvents = append(f.FlushedEvents, events) 16 | return nil 17 | } 18 | 19 | func mockEvent() Event { 20 | return CallEvent{ 21 | Time: time.Now(), 22 | Duration: time.Duration(1 * time.Millisecond), 23 | } 24 | } 25 | 26 | func mockTraceEvent() TraceEvent { 27 | var evts []Event 28 | evts = append(evts, mockEvent()) 29 | evts = append(evts, mockEvent()) 30 | evts = append(evts, mockEvent()) 31 | 32 | return TraceEvent{ 33 | TelemetryId: NewTraceId(), 34 | AdapterMeta: nil, 35 | Events: evts, 36 | } 37 | } 38 | 39 | func TestBucket(t *testing.T) { 40 | flusher := &TestFlusher{} 41 | bucket := NewEventBucket(2, time.Duration(500*time.Millisecond)) 42 | 43 | bucket.addEvent(mockTraceEvent(), flusher) 44 | bucket.addEvent(mockTraceEvent(), flusher) 45 | bucket.addEvent(mockTraceEvent(), flusher) 46 | 47 | time.Sleep(time.Duration(250 * time.Millisecond)) 48 | 49 | if flusher.NumFlushes > 0 { 50 | t.Fatalf("After 0.25 seconds, no events should be flushed but there were %d flushes", flusher.NumFlushes) 51 | } 52 | 53 | time.Sleep(time.Duration(500 * time.Millisecond)) 54 | 55 | if flusher.NumFlushes != 2 { 56 | t.Fatalf("After .75 seconds, there should have been exactly 2 flushes but there were %d flushes", flusher.NumFlushes) 57 | } 58 | 59 | evts1 := flusher.FlushedEvents[0] 60 | if len(evts1) != 2 { 61 | t.Fatalf("Expected 2 TraceEvents but got %d", len(evts1)) 62 | } 63 | evts2 := flusher.FlushedEvents[1] 64 | if len(evts2) != 1 { 65 | t.Fatalf("Expected 1 but got %d", len(evts2)) 66 | } 67 | 68 | if len(bucket.bucket) != 0 { 69 | t.Fatalf("Expected the event bucket to be empty but there were %d traceevents", len(bucket.bucket)) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dylibso/observe-sdk/go 2 | 3 | go 1.21 4 | 5 | toolchain go1.22.2 6 | 7 | require ( 8 | github.com/ianlancetaylor/demangle v0.0.0-20240805132620-81f5be970eca 9 | github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 10 | github.com/tetratelabs/wazero v1.8.0 11 | ) 12 | 13 | require ( 14 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 15 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 16 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 17 | go.opentelemetry.io/proto/otlp v1.3.1 18 | google.golang.org/protobuf v1.34.2 19 | ) 20 | 21 | require ( 22 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 23 | github.com/go-logr/logr v1.4.2 // indirect 24 | github.com/go-logr/stdr v1.2.2 // indirect 25 | github.com/golang/protobuf v1.5.4 // indirect 26 | github.com/google/uuid v1.6.0 // indirect 27 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 // indirect 28 | go.opentelemetry.io/otel v1.28.0 // indirect 29 | go.opentelemetry.io/otel/metric v1.28.0 // indirect 30 | go.opentelemetry.io/otel/sdk v1.28.0 // indirect 31 | go.opentelemetry.io/otel/trace v1.28.0 // indirect 32 | golang.org/x/net v0.28.0 // indirect 33 | golang.org/x/sys v0.24.0 // indirect 34 | golang.org/x/text v0.17.0 // indirect 35 | google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect 36 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect 37 | google.golang.org/grpc v1.65.0 // indirect 38 | ) 39 | -------------------------------------------------------------------------------- /go/listener.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/tetratelabs/wazero/api" 7 | "github.com/tetratelabs/wazero/experimental" 8 | ) 9 | 10 | // Implements the NewListener() method to satisfy the FunctionListener interface 11 | func (t *TraceCtx) NewListener(def api.FunctionDefinition) experimental.FunctionListener { 12 | if def.GoFunction() == nil { 13 | return nil 14 | } 15 | return t 16 | } 17 | 18 | // Implements the NewFunctionListener() method to satisfy the FunctionListener interface 19 | func (t *TraceCtx) NewFunctionListener(_ api.FunctionDefinition) experimental.FunctionListener { 20 | return t 21 | } 22 | 23 | // Implements the Before() method to satisfy the FunctionListener interface. 24 | // This takes events from the wazero runtime and sends them to the `raw` channel on the TraceCtx. 25 | func (t *TraceCtx) Before(ctx context.Context, _ api.Module, def api.FunctionDefinition, inputs []uint64, stack experimental.StackIterator) { 26 | var event RawEvent 27 | name := def.Name() 28 | 29 | switch name { 30 | case "enter": 31 | fallthrough 32 | case "instrument_enter": 33 | event.Kind = RawEnter 34 | event.FunctionIndex = uint32(inputs[0]) 35 | event.FunctionName = t.names[event.FunctionIndex] 36 | case "exit": 37 | fallthrough 38 | case "instrument_exit": 39 | event.Kind = RawExit 40 | event.FunctionIndex = uint32(inputs[0]) 41 | event.FunctionName = t.names[event.FunctionIndex] 42 | case "memory-grow": 43 | fallthrough 44 | case "instrument_memory_grow": 45 | event.Kind = RawMemoryGrow 46 | event.MemoryGrowAmount = uint32(inputs[0]) 47 | 48 | // manual events 49 | case "span-enter": 50 | fallthrough 51 | case "span_enter": 52 | event.Kind = RawEnter 53 | case "span-exit": 54 | fallthrough 55 | case "span_exit": 56 | event.Kind = RawExit 57 | case "span-tags": 58 | fallthrough 59 | case "span_tags": 60 | event.Kind = RawSpanTags 61 | case "metric": 62 | return 63 | case "log": 64 | return 65 | default: 66 | event.Kind = RawUnknownEvent 67 | } 68 | for stack.Next() { 69 | f := stack.Function() 70 | event.Stack = append(event.Stack, f) 71 | } 72 | t.raw <- event 73 | } 74 | 75 | // Null implementation of the After() method to satisfy the FunctionListener interface. 76 | func (t *TraceCtx) After(context.Context, api.Module, api.FunctionDefinition, []uint64) {} 77 | 78 | // Null implementation of the Abort() method to satisfy the FunctionListener interface. 79 | func (t *TraceCtx) Abort(context.Context, api.Module, api.FunctionDefinition, error) {} 80 | -------------------------------------------------------------------------------- /go/telemetry.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | // This is a shared type for a span or trace id. 13 | // It's represented by 2 uint64s and can be transformed 14 | // to different string or int representations where needed. 15 | type TelemetryId struct { 16 | lsb uint64 17 | msb uint64 18 | } 19 | 20 | var rng rand.Source 21 | 22 | func init() { 23 | rng = rand.NewSource(time.Now().UnixNano()) 24 | } 25 | 26 | // Create a new trace id 27 | func NewTraceId() TelemetryId { 28 | return TelemetryId{ 29 | msb: uint64(rng.Int63()), 30 | lsb: uint64(rng.Int63()), 31 | } 32 | } 33 | 34 | // Create a new span id 35 | func NewSpanId() TelemetryId { 36 | return TelemetryId{ 37 | msb: uint64(rng.Int63()), 38 | lsb: uint64(rng.Int63()), 39 | } 40 | } 41 | 42 | func (id TelemetryId) Msb() uint64 { 43 | return id.msb 44 | } 45 | 46 | func (id TelemetryId) Lsb() uint64 { 47 | return id.lsb 48 | } 49 | 50 | // Encode this id into an 8 byte hex (16 chars) 51 | // Just uses the least significant of the 16 bytes 52 | func (t TelemetryId) ToHex8() string { 53 | return fmt.Sprintf("%016x", t.lsb) 54 | } 55 | 56 | // Encode this id into a 16 byte hex (32 chars) 57 | // Uses both 16 byte uint64 values 58 | func (t TelemetryId) ToHex16() string { 59 | return fmt.Sprintf("%016x%016x", t.msb, t.lsb) 60 | } 61 | 62 | // Some adapters may need a raw representation 63 | func (t TelemetryId) ToUint64() uint64 { 64 | return t.lsb 65 | } 66 | 67 | func (t *TelemetryId) FromBytes(id []byte) error { 68 | if len(id) != 16 { 69 | return errors.New("TraceID must be 16 bytes") 70 | } 71 | 72 | t.msb = binary.BigEndian.Uint64(id) 73 | t.lsb = binary.BigEndian.Uint64(id[8:]) 74 | 75 | return nil 76 | } 77 | 78 | func (t *TelemetryId) FromString(id string) error { 79 | b, err := hex.DecodeString(id) 80 | if err != nil { 81 | return err 82 | } 83 | return t.FromBytes(b) 84 | } 85 | -------------------------------------------------------------------------------- /go/trace_ctx_test.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestTraceId(t *testing.T) { 9 | a := NewTraceId() 10 | 11 | b := TelemetryId{} 12 | if err := b.FromString(a.ToHex16()); err != nil { 13 | t.Error(err) 14 | } 15 | 16 | fmt.Println(a.ToHex16(), b.ToHex16()) 17 | fmt.Println(a, b) 18 | 19 | if a != b { 20 | t.Fail() 21 | } 22 | 23 | err := b.FromString(a.ToHex16() + "a") 24 | if err == nil { 25 | t.Fail() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /go/wasm.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | 7 | "github.com/tetratelabs/wabin/binary" 8 | "github.com/tetratelabs/wabin/wasm" 9 | ) 10 | 11 | // Parse the names of the functions out of the 12 | // names custom section in the wasm binary. 13 | func parseNames(data []byte) (map[uint32]string, error) { 14 | features := wasm.CoreFeaturesV2 15 | m, err := binary.DecodeModule(data, features) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | if m.NameSection == nil { 21 | return nil, errors.New("Name section not found") 22 | } 23 | 24 | names := make(map[uint32]string, len(m.NameSection.FunctionNames)) 25 | 26 | for _, v := range m.NameSection.FunctionNames { 27 | names[v.Index] = v.Name 28 | } 29 | 30 | warnOnDylibsoObserve := true 31 | for _, item := range m.ImportSection { 32 | if item.Module == "dylibso_observe" { 33 | if warnOnDylibsoObserve { 34 | warnOnDylibsoObserve = false 35 | log.Println("Module uses deprecated namespace \"dylibso_observe\"!\n" + 36 | "Please consider reinstrumenting with newer wasm-instr or Observe API!") 37 | } 38 | } 39 | } 40 | 41 | return names, nil 42 | } 43 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@bjorn3/browser_wasi_shim": "^0.2.14", 4 | "commander": "^11.0.0", 5 | "esbuild": "^0.18.13", 6 | "typescript": "^5.1.6", 7 | "wasi": "^0.0.6" 8 | }, 9 | "dependencies": { 10 | "protobufjs": "^7.2.4" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .github/ 3 | .npmignore 4 | .travis.yml 5 | badge.png 6 | badge.svg 7 | CONTRIBUTING.md 8 | docs/ 9 | docs/logos/ 10 | SECURITY.md 11 | sticker.png 12 | sticker.svg 13 | test/ 14 | tmp/ 15 | tools/ 16 | *.code-workspace 17 | .env 18 | .vscode 19 | esbuild/ 20 | test-data/ -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | DatadogAdapter, 3 | type DatadogConfig, 4 | DefaultDatadogConfig, 5 | } from '../../src/lib/adapters/datadog/mod.ts'; 6 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-datadog", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@dylibso/observe-sdk-datadog", 9 | "version": "0.2.0", 10 | "license": "ISC" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-datadog", 3 | "version": "0.2.0", 4 | "description": "A library that produces function tracing to datadog", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "main": "./dist/cjs/index.js", 9 | "module": "./dist/esm/index.js", 10 | "types": "./dist/types/index.d.ts", 11 | "exports": { 12 | "workerd": "./dist/workerd/index.js", 13 | "default": { 14 | "types": "./dist/types/index.d.ts", 15 | "import": "./dist/esm/index.js", 16 | "require": "./dist/cjs/index.js" 17 | } 18 | }, 19 | "scripts": { 20 | "build:esm": "node ../../esbuild/esbuild.js -b -e ./index.js -o ../observe-sdk-datadog/dist/esm/index.js -p browser -f esm", 21 | "build:cjs": "node ../../esbuild/esbuild.js -b -e ./index.js -o ../observe-sdk-datadog/dist/cjs/index.js -p browser -f cjs", 22 | "build:workerd": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/workerd/index.js -p browser -f esm --workerd", 23 | "build:types": "tsc -b", 24 | "build": "npm run build:esm && npm run build:cjs && npm run build:workerd && npm run build:types", 25 | "build:web-test": "node ../../esbuild/esbuild.js -b -e ./test/web/index.js -o ./test/web/build.js -p browser", 26 | "test:node": "node test/node/index.js", 27 | "test:deno": "deno run -A test/deno/index.ts", 28 | "test:web": "npm run build:web-test && npx serve ./test/web", 29 | "test:workerd": "npx wrangler dev -c ./test/workerd/wrangler.toml" 30 | }, 31 | "keywords": [ 32 | "dylibso", 33 | "datadog", 34 | "tracing", 35 | "observe", 36 | "opentelemetry", 37 | "otel", 38 | "wasm", 39 | "webassembly" 40 | ], 41 | "author": "", 42 | "license": "ISC" 43 | } 44 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/deno/index.ts: -------------------------------------------------------------------------------- 1 | import { DatadogAdapter } from "../../dist/esm/index.js"; 2 | import Context from "https://deno.land/std@0.192.0/wasi/snapshot_preview1.ts"; 3 | 4 | const adapter = new DatadogAdapter(); 5 | 6 | const bytes = await Deno.readFile("../../test-data/test.c.instr.wasm"); 7 | 8 | const opts = { 9 | spanFilter: { 10 | minDurationMicroseconds: 100, 11 | } 12 | }; 13 | const traceContext = await adapter.start(bytes, opts); 14 | const module = new WebAssembly.Module(bytes); 15 | 16 | const runtime = new Context({ 17 | stdin: Deno.stdin.rid, 18 | stdout: Deno.stdout.rid, 19 | }); 20 | const instance = new WebAssembly.Instance( 21 | module, 22 | { 23 | "wasi_snapshot_preview1": runtime.exports, 24 | ...traceContext.getImportObject(), 25 | }, 26 | ); 27 | runtime.start(instance); 28 | 29 | traceContext.stop(); 30 | 31 | setTimeout(() => { }, 3000); 32 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/node/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { WASI } = require("wasi"); 3 | const { env, argv } = require("node:process"); 4 | const { DatadogAdapter } = require("@dylibso/observe-sdk-datadog"); 5 | 6 | const wasi = new WASI({ 7 | version: "preview1", 8 | args: argv.slice(1), 9 | env, 10 | }); 11 | 12 | const adapter = new DatadogAdapter(); 13 | const opts = { 14 | spanFilter: { 15 | minDurationMicroseconds: 100, 16 | } 17 | }; 18 | 19 | const bytes = fs.readFileSync("../../test-data/test.c.instr.wasm"); 20 | 21 | adapter.start(bytes, opts).then((traceContext) => { 22 | const module = new WebAssembly.Module(bytes); 23 | 24 | WebAssembly.instantiate(module, { 25 | ...wasi.getImportObject(), 26 | ...traceContext.getImportObject(), 27 | }).then((instance) => { 28 | wasi.start(instance); 29 | traceContext.setMetadata({ 30 | http_status_code: 200, 31 | http_url: "https://example.com", 32 | }); 33 | traceContext.stop(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-datadog": "file:../.." 13 | } 14 | }, 15 | "../..": { 16 | "version": "0.1.3", 17 | "license": "ISC" 18 | }, 19 | "node_modules/@dylibso/observe-sdk-datadog": { 20 | "resolved": "../..", 21 | "link": true 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-datadog": "file:../.." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/web/count_vowels.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-datadog/test/web/count_vowels.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/web/index.js: -------------------------------------------------------------------------------- 1 | import { DatadogAdapter } from "@dylibso/observe-sdk-datadog"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | 4 | const f = async () => { 5 | const adapter = new DatadogAdapter(); 6 | const opts = { 7 | spanFilter: { 8 | minDurationMicroseconds: 100, 9 | } 10 | }; 11 | const resp = await fetch("count_vowels.instr.wasm"); 12 | 13 | const bytes = await resp.arrayBuffer(); 14 | const traceContext = await adapter.start(bytes, opts); 15 | 16 | let fds = [ 17 | new OpenFile( 18 | new File( 19 | new TextEncoder("utf-8").encode(`count these vowels for me please`), 20 | ), 21 | ), // stdin 22 | new OpenFile(new File([])), // stdout 23 | new OpenFile(new File([])), // stderr 24 | ]; 25 | let wasi = new WASI([], [], fds); 26 | const instance = await WebAssembly.instantiate(bytes, { 27 | "wasi_snapshot_preview1": wasi.wasiImport, 28 | ...traceContext.getImportObject(), 29 | }); 30 | 31 | wasi.start(instance.instance); 32 | let utf8decoder = new TextDecoder(); 33 | console.log(utf8decoder.decode(fds[1].file.data)); 34 | traceContext.stop(); 35 | }; 36 | 37 | f().then(() => { }); 38 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dylibso-observe-sdk-datadog", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev" 9 | }, 10 | "devDependencies": { 11 | "@cloudflare/workers-types": "^4.20231218.0", 12 | "typescript": "^5.0.4", 13 | "wrangler": "^3.22.2" 14 | }, 15 | "dependencies": { 16 | "@bjorn3/browser_wasi_shim": "^0.2.17", 17 | "@dylibso/observe-sdk-datadog": "file:../.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/src/code.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-datadog/test/workerd/src/code.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/src/index.ts: -------------------------------------------------------------------------------- 1 | import { DatadogAdapter } from "@dylibso/observe-sdk-datadog"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | import code from './code.wasm'; 4 | 5 | export interface Env { 6 | HONEYCOMB_API_KEY: string; 7 | } 8 | 9 | export default { 10 | async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise { 11 | // create a new instance of the adapter with the config, this should be shared across requests 12 | const adapter = new DatadogAdapter(); 13 | 14 | // setup some files for stdin, stdout, and stderr 15 | let fds = [ 16 | new OpenFile( 17 | new File( 18 | new TextEncoder().encode(await req.text()), 19 | ), 20 | ), // stdin 21 | new OpenFile(new File([])), // stdout 22 | new OpenFile(new File([])), // stderr 23 | ]; 24 | 25 | // instantiate the wasm module 26 | let wasi = new WASI([], [], fds); 27 | 28 | // start the adapter with the wasm module bytes and options 29 | const traceContext = await adapter.start(code, {}); 30 | 31 | // create a new instance of the wasm module using a new trace context to record observability data 32 | const instance = await WebAssembly.instantiate(code, { 33 | "wasi_snapshot_preview1": wasi.wasiImport, 34 | ...traceContext.getImportObject(), 35 | }); 36 | 37 | // execute the module 38 | wasi.start(instance); 39 | let dec = new TextDecoder(); 40 | const output = dec.decode(fds[1].file.data); 41 | 42 | traceContext.stop(); 43 | await adapter.send() 44 | 45 | return new Response(output) 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/test/workerd/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "dylibso-observe-sdk-datadog" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-12-18" 4 | 5 | # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) 6 | # Note: Use secrets to store sensitive data. 7 | # Docs: https://developers.cloudflare.com/workers/platform/environment-variables 8 | # [vars] 9 | # MY_VARIABLE = "production_value" 10 | 11 | # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 12 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/kv 13 | # [[kv_namespaces]] 14 | # binding = "MY_KV_NAMESPACE" 15 | # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 16 | 17 | # Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. 18 | # Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ 19 | # [[r2_buckets]] 20 | # binding = "MY_BUCKET" 21 | # bucket_name = "my-bucket" 22 | 23 | # Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. 24 | # Docs: https://developers.cloudflare.com/queues/get-started 25 | # [[queues.producers]] 26 | # binding = "MY_QUEUE" 27 | # queue = "my-queue" 28 | 29 | # Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. 30 | # Docs: https://developers.cloudflare.com/queues/get-started 31 | # [[queues.consumers]] 32 | # queue = "my-queue" 33 | 34 | # Bind another Worker service. Use this binding to call another Worker without network overhead. 35 | # Docs: https://developers.cloudflare.com/workers/platform/services 36 | # [[services]] 37 | # binding = "MY_SERVICE" 38 | # service = "my-service" 39 | 40 | # Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. 41 | # Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. 42 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects 43 | # [[durable_objects.bindings]] 44 | # name = "MY_DURABLE_OBJECT" 45 | # class_name = "MyDurableObject" 46 | 47 | # Durable Object migrations. 48 | # Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations 49 | # [[migrations]] 50 | # tag = "v1" 51 | # new_classes = ["MyDurableObject"] 52 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-datadog/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "index.ts", 4 | ], 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "dist/types", 10 | "outFile": "dist/types/index.d.ts", 11 | "declarationMap": true, 12 | "allowImportingTsExtensions": true, 13 | "lib": [ 14 | "DOM", 15 | "ESNext", 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .github/ 3 | .npmignore 4 | .travis.yml 5 | badge.png 6 | badge.svg 7 | CONTRIBUTING.md 8 | docs/ 9 | docs/logos/ 10 | SECURITY.md 11 | sticker.png 12 | sticker.svg 13 | test/ 14 | tmp/ 15 | tools/ 16 | *.code-workspace 17 | .env 18 | .vscode 19 | esbuild/ 20 | test-data/ -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | HoneycombAdapter, 3 | type HoneycombConfig, 4 | } from '../../src/lib/adapters/honeycomb/mod.ts'; 5 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-honeycomb", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@dylibso/observe-sdk-honeycomb", 9 | "version": "0.2.0", 10 | "license": "ISC" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-honeycomb", 3 | "version": "0.2.0", 4 | "description": "A library that produces function tracing to honeycomb", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "main": "./dist/cjs/index.js", 9 | "module": "./dist/esm/index.js", 10 | "types": "./dist/types/index.d.ts", 11 | "exports": { 12 | "workerd": "./dist/workerd/index.js", 13 | "default": { 14 | "types": "./dist/types/index.d.ts", 15 | "import": "./dist/esm/index.js", 16 | "require": "./dist/cjs/index.js" 17 | } 18 | }, 19 | "scripts": { 20 | "build:esm": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/esm/index.js -p browser -f esm", 21 | "build:cjs": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/cjs/index.js -p browser -f cjs", 22 | "build:workerd": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/workerd/index.js -p browser -f esm --workerd", 23 | "build:types": "tsc -b", 24 | "build": "npm run build:esm && npm run build:cjs && npm run build:workerd && npm run build:types", 25 | "build:web-test": "node ../../esbuild/esbuild.js -b -e ./test/web/index.js -o ./test/web/build.js -p browser", 26 | "test:node": "node test/node/index.js", 27 | "test:deno": "deno run -A test/deno/index.ts", 28 | "test:web": "npm run build:web-test && npx serve ./test/web", 29 | "test:workerd": "npx wrangler dev -c ./test/workerd/wrangler.toml" 30 | }, 31 | "keywords": [ 32 | "dylibso", 33 | "honeycomb", 34 | "tracing", 35 | "observe", 36 | "opentelemetry", 37 | "otel", 38 | "wasm", 39 | "webassembly" 40 | ], 41 | "author": "", 42 | "license": "ISC" 43 | } 44 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/deno/index.ts: -------------------------------------------------------------------------------- 1 | import { HoneycombAdapter, HoneycombConfig } from "../../dist/esm/index.js"; 2 | import Context from "https://deno.land/std@0.192.0/wasi/snapshot_preview1.ts"; 3 | import { load } from "https://deno.land/std/dotenv/mod.ts"; 4 | 5 | const env = await load(); 6 | const apiKey = env["HONEYCOMB_API_KEY"]; 7 | 8 | const config: HoneycombConfig = { 9 | apiKey: apiKey, 10 | dataset: 'deno', 11 | emitTracesInterval: 1000, 12 | traceBatchMax: 100, 13 | host: 'https://api.honeycomb.io', 14 | } 15 | const adapter = new HoneycombAdapter(config); 16 | const opts = { 17 | spanFilter: { 18 | minDurationMicroseconds: 100, 19 | } 20 | }; 21 | 22 | const bytes = await Deno.readFile("../../test-data/test.c.instr.wasm"); 23 | const traceContext = await adapter.start(bytes, opts); 24 | const module = new WebAssembly.Module(bytes); 25 | 26 | const runtime = new Context({ 27 | stdin: Deno.stdin.rid, 28 | stdout: Deno.stdout.rid, 29 | }); 30 | const instance = new WebAssembly.Instance( 31 | module, 32 | { 33 | "wasi_snapshot_preview1": runtime.exports, 34 | ...traceContext.getImportObject(), 35 | }, 36 | ); 37 | runtime.start(instance); 38 | 39 | traceContext.stop(); 40 | 41 | setTimeout(() => { }, 3000); 42 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/node/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { WASI } = require("wasi"); 3 | const { env, argv } = require('node:process'); 4 | const { HoneycombAdapter } = require("@dylibso/observe-sdk-honeycomb"); 5 | require('dotenv').config(); 6 | 7 | const wasi = new WASI({ 8 | version: "preview1", 9 | args: argv.slice(1), 10 | env, 11 | }); 12 | 13 | const config = { 14 | apiKey: process.env.HONEYCOMB_API_KEY, 15 | dataset: 'node', 16 | emitTracesInterval: 1000, 17 | traceBatchMax: 100, 18 | host: 'https://api.honeycomb.io', 19 | } 20 | const adapter = new HoneycombAdapter(config); 21 | const opts = { 22 | spanFilter: { 23 | minDurationMicroseconds: 100, 24 | } 25 | }; 26 | 27 | const bytes = fs.readFileSync("../../test-data/test.c.instr.wasm"); 28 | adapter.start(bytes, opts).then((traceContext) => { 29 | const module = new WebAssembly.Module(bytes); 30 | 31 | WebAssembly.instantiate(module, { 32 | ...wasi.getImportObject(), 33 | ...traceContext.getImportObject(), 34 | }).then((instance) => { 35 | wasi.start(instance); 36 | // adapter.setMetadata({ 37 | // http_status_code: 200, 38 | // http_url: "https://example.com", 39 | // }); 40 | traceContext.stop(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-honeycomb": "file:../..", 13 | "dotenv": "^16.3.1" 14 | } 15 | }, 16 | "../..": { 17 | "name": "@dylibso/observe-sdk-honeycomb", 18 | "version": "0.1.3", 19 | "license": "ISC" 20 | }, 21 | "node_modules/@dylibso/observe-sdk-honeycomb": { 22 | "resolved": "../..", 23 | "link": true 24 | }, 25 | "node_modules/dotenv": { 26 | "version": "16.3.1", 27 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 28 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 29 | "engines": { 30 | "node": ">=12" 31 | }, 32 | "funding": { 33 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-honeycomb": "file:../..", 13 | "dotenv": "^16.3.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/count_vowels.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-honeycomb/test/web/count_vowels.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/index.js: -------------------------------------------------------------------------------- 1 | import { HoneycombAdapter } from "@dylibso/observe-sdk-honeycomb"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | 4 | const f = async () => { 5 | const config = { 6 | apiKey: 'YOUR_API_KEY_HERE', 7 | dataset: 'web', 8 | emitTracesInterval: 1000, 9 | traceBatchMax: 100, 10 | host: 'https://api.honeycomb.io', 11 | } 12 | const adapter = new HoneycombAdapter(config); 13 | const opts = { 14 | spanFilter: { 15 | minDurationMicroseconds: 100, 16 | } 17 | }; 18 | const resp = await fetch("test.c.instr.wasm"); 19 | 20 | const bytes = await resp.arrayBuffer(); 21 | const traceContext = await adapter.start(bytes, opts); 22 | 23 | let fds = [ 24 | new OpenFile( 25 | new File( 26 | new TextEncoder("utf-8").encode(`count these vowels for me please`), 27 | ), 28 | ), // stdin 29 | new OpenFile(new File([])), // stdout 30 | new OpenFile(new File([])), // stderr 31 | ]; 32 | let wasi = new WASI([], [], fds); 33 | const instance = await WebAssembly.instantiate(bytes, { 34 | "wasi_snapshot_preview1": wasi.wasiImport, 35 | ...traceContext.getImportObject(), 36 | }); 37 | 38 | wasi.start(instance.instance); 39 | let utf8decoder = new TextDecoder(); 40 | console.log(utf8decoder.decode(fds[1].file.data)); 41 | traceContext.stop(); 42 | }; 43 | 44 | f().then(() => { }); 45 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.3.1" 13 | } 14 | }, 15 | "../..": { 16 | "name": "@dylibso/observe-sdk-honeycomb", 17 | "version": "1.0.0", 18 | "extraneous": true, 19 | "license": "ISC" 20 | }, 21 | "node_modules/dotenv": { 22 | "version": "16.3.1", 23 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 24 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 25 | "engines": { 26 | "node": ">=12" 27 | }, 28 | "funding": { 29 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.3.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/web/test.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-honeycomb/test/web/test.c.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dylibso-observe-sdk-honeycomb", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev" 9 | }, 10 | "devDependencies": { 11 | "@cloudflare/workers-types": "^4.20231218.0", 12 | "typescript": "^5.0.4", 13 | "wrangler": "^3.22.2" 14 | }, 15 | "dependencies": { 16 | "@bjorn3/browser_wasi_shim": "^0.2.17", 17 | "@dylibso/observe-sdk-honeycomb": "file:../.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/src/code.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-honeycomb/test/workerd/src/code.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/src/index.ts: -------------------------------------------------------------------------------- 1 | import { HoneycombAdapter } from "@dylibso/observe-sdk-honeycomb"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | import code from './code.wasm'; 4 | 5 | export interface Env { 6 | HONEYCOMB_API_KEY: string; 7 | } 8 | 9 | export default { 10 | async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise { 11 | // setup some custom configuration for the adapter 12 | const config = { 13 | // loaded as a Secret defined in the Worker, 14 | // see: https://developers.cloudflare.com/workers/wrangler/configuration/#environmental-variables 15 | apiKey: env.HONEYCOMB_API_KEY, 16 | dataset: "cloudflare-worker", 17 | emitTracesInterval: 100, 18 | traceBatchMax: 100, 19 | host: "https://api.honeycomb.io", 20 | }; 21 | 22 | // create a new instance of the adapter with the config, this should be shared across requests 23 | const adapter = new HoneycombAdapter(config); 24 | 25 | // setup some files for stdin, stdout, and stderr 26 | let fds = [ 27 | new OpenFile( 28 | new File( 29 | new TextEncoder().encode(await req.text()), 30 | ), 31 | ), // stdin 32 | new OpenFile(new File([])), // stdout 33 | new OpenFile(new File([])), // stderr 34 | ]; 35 | 36 | // instantiate the wasm module 37 | let wasi = new WASI([], [], fds); 38 | 39 | // start the adapter with the wasm module bytes and options 40 | const traceContext = await adapter.start(code, {}); 41 | 42 | // create a new instance of the wasm module using a new trace context to record observability data 43 | const instance = await WebAssembly.instantiate(code, { 44 | "wasi_snapshot_preview1": wasi.wasiImport, 45 | ...traceContext.getImportObject(), 46 | }); 47 | 48 | // execute the module 49 | wasi.start(instance); 50 | let dec = new TextDecoder(); 51 | const output = dec.decode(fds[1].file.data); 52 | 53 | traceContext.stop(); 54 | await adapter.send(); 55 | 56 | return new Response(output) 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/test/workerd/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "dylibso-observe-sdk-honeycomb" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-12-18" 4 | 5 | # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) 6 | # Note: Use secrets to store sensitive data. 7 | # Docs: https://developers.cloudflare.com/workers/platform/environment-variables 8 | # [vars] 9 | # MY_VARIABLE = "production_value" 10 | 11 | # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 12 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/kv 13 | # [[kv_namespaces]] 14 | # binding = "MY_KV_NAMESPACE" 15 | # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 16 | 17 | # Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. 18 | # Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ 19 | # [[r2_buckets]] 20 | # binding = "MY_BUCKET" 21 | # bucket_name = "my-bucket" 22 | 23 | # Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. 24 | # Docs: https://developers.cloudflare.com/queues/get-started 25 | # [[queues.producers]] 26 | # binding = "MY_QUEUE" 27 | # queue = "my-queue" 28 | 29 | # Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. 30 | # Docs: https://developers.cloudflare.com/queues/get-started 31 | # [[queues.consumers]] 32 | # queue = "my-queue" 33 | 34 | # Bind another Worker service. Use this binding to call another Worker without network overhead. 35 | # Docs: https://developers.cloudflare.com/workers/platform/services 36 | # [[services]] 37 | # binding = "MY_SERVICE" 38 | # service = "my-service" 39 | 40 | # Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. 41 | # Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. 42 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects 43 | # [[durable_objects.bindings]] 44 | # name = "MY_DURABLE_OBJECT" 45 | # class_name = "MyDurableObject" 46 | 47 | # Durable Object migrations. 48 | # Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations 49 | # [[migrations]] 50 | # tag = "v1" 51 | # new_classes = ["MyDurableObject"] 52 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-honeycomb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "index.ts", 4 | ], 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "dist/types", 10 | "outFile": "dist/types/index.d.ts", 11 | "declarationMap": true, 12 | "allowImportingTsExtensions": true, 13 | "lib": [ 14 | "DOM", 15 | "ESNext", 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .github/ 3 | .npmignore 4 | .travis.yml 5 | badge.png 6 | badge.svg 7 | CONTRIBUTING.md 8 | docs/ 9 | docs/logos/ 10 | SECURITY.md 11 | sticker.png 12 | sticker.svg 13 | test/ 14 | tmp/ 15 | tools/ 16 | *.code-workspace 17 | .env 18 | .vscode 19 | esbuild/ 20 | test-data/ -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | LightstepAdapter, 3 | type LightstepConfig, 4 | } from '../../src/lib/adapters/lightstep/mod.ts'; 5 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-lightstep", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@dylibso/observe-sdk-lightstep", 9 | "version": "0.2.0", 10 | "license": "ISC" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-lightstep", 3 | "version": "0.2.0", 4 | "description": "A library that produces function tracing to lightstep", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "main": "./dist/cjs/index.js", 9 | "module": "./dist/esm/index.js", 10 | "types": "./dist/types/index.d.ts", 11 | "exports": { 12 | "workerd": "./dist/workerd/index.js", 13 | "default": { 14 | "types": "./dist/types/index.d.ts", 15 | "import": "./dist/esm/index.js", 16 | "require": "./dist/cjs/index.js" 17 | } 18 | }, 19 | "scripts": { 20 | "build:esm": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/esm/index.js -p browser -f esm", 21 | "build:cjs": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/cjs/index.js -p browser -f cjs", 22 | "build:workerd": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/workerd/index.js -p browser -f esm --workerd", 23 | "build:types": "tsc -b", 24 | "build": "npm run build:esm && npm run build:cjs && npm run build:workerd && npm run build:types", 25 | "build:web-test": "node ../../esbuild/esbuild.js -b -e ./test/web/index.js -o ./test/web/build.js -p browser", 26 | "test:node": "node test/node/index.js", 27 | "test:deno": "deno run -A test/deno/index.ts", 28 | "test:web": "npm run build:web-test && npx serve ./test/web", 29 | "test:workerd": "npx wrangler dev -c ./test/workerd/wrangler.toml" 30 | }, 31 | "keywords": [ 32 | "dylibso", 33 | "lightstep", 34 | "tracing", 35 | "observe", 36 | "opentelemetry", 37 | "otel", 38 | "wasm", 39 | "webassembly" 40 | ], 41 | "author": "", 42 | "license": "ISC" 43 | } 44 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/deno/index.ts: -------------------------------------------------------------------------------- 1 | import { LightstepAdapter, LightstepConfig } from "../../dist/esm/index.js"; 2 | import Context from "https://deno.land/std@0.192.0/wasi/snapshot_preview1.ts"; 3 | import { load } from "https://deno.land/std/dotenv/mod.ts"; 4 | 5 | const env = await load(); 6 | const apiKey = env["LIGHTSTEP_API_KEY"]; 7 | 8 | const config: LightstepConfig = { 9 | apiKey: apiKey, 10 | serviceName: 'deno', 11 | emitTracesInterval: 1000, 12 | traceBatchMax: 100, 13 | host: 'https://ingest.lightstep.com', 14 | } 15 | const adapter = new LightstepAdapter(config); 16 | const opts = { 17 | spanFilter: { 18 | minDurationMicroseconds: 100, 19 | } 20 | }; 21 | const bytes = await Deno.readFile("../../test-data/test.c.instr.wasm"); 22 | const traceContext = await adapter.start(bytes, opts); 23 | const module = new WebAssembly.Module(bytes); 24 | 25 | const runtime = new Context({ 26 | stdin: Deno.stdin.rid, 27 | stdout: Deno.stdout.rid, 28 | }); 29 | const instance = new WebAssembly.Instance( 30 | module, 31 | { 32 | "wasi_snapshot_preview1": runtime.exports, 33 | ...traceContext.getImportObject(), 34 | }, 35 | ); 36 | runtime.start(instance); 37 | 38 | traceContext.stop(); 39 | 40 | setTimeout(() => { }, 3000); 41 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/node/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { WASI } = require("wasi"); 3 | const { env, argv } = require('node:process'); 4 | const { LightstepAdapter } = require("@dylibso/observe-sdk-lightstep"); 5 | require('dotenv').config(); 6 | 7 | const wasi = new WASI({ 8 | version: "preview1", 9 | args: argv.slice(1), 10 | env, 11 | }); 12 | 13 | const config = { 14 | apiKey: process.env.LIGHTSTEP_API_KEY, 15 | serviceName: 'node', 16 | emitTracesInterval: 1000, 17 | traceBatchMax: 100, 18 | host: 'https://ingest.lightstep.com', 19 | } 20 | const adapter = new LightstepAdapter(config); 21 | const opts = { 22 | spanFilter: { 23 | minDurationMicroseconds: 100, 24 | } 25 | }; 26 | 27 | const bytes = fs.readFileSync("../../test-data/test.c.instr.wasm"); 28 | adapter.start(bytes, opts).then((traceContext) => { 29 | const module = new WebAssembly.Module(bytes); 30 | 31 | WebAssembly.instantiate(module, { 32 | ...wasi.getImportObject(), 33 | ...traceContext.getImportObject(), 34 | }).then((instance) => { 35 | wasi.start(instance); 36 | // adapter.setMetadata({ 37 | // http_status_code: 200, 38 | // http_url: "https://example.com", 39 | // }); 40 | traceContext.stop(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-lightstep": "file:../..", 13 | "dotenv": "^16.3.1" 14 | } 15 | }, 16 | "../..": { 17 | "name": "@dylibso/observe-sdk-honeycomb", 18 | "version": "1.0.0", 19 | "license": "ISC" 20 | }, 21 | "node_modules/@dylibso/observe-sdk-lightstep": { 22 | "resolved": "../..", 23 | "link": true 24 | }, 25 | "node_modules/dotenv": { 26 | "version": "16.3.1", 27 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 28 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 29 | "engines": { 30 | "node": ">=12" 31 | }, 32 | "funding": { 33 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.3.1", 13 | "@dylibso/observe-sdk-lightstep": "file:../.." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/count_vowels.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-lightstep/test/web/count_vowels.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/index.js: -------------------------------------------------------------------------------- 1 | import { LightstepAdapter } from "@dylibso/observe-sdk-lightstep"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | 4 | const f = async () => { 5 | const config = { 6 | apiKey: 'YOUR_API_KEY_HERE', 7 | serviceName: 'web', 8 | emitTracesInterval: 1000, 9 | traceBatchMax: 100, 10 | host: 'https://ingest.lightstep.com', 11 | } 12 | const adapter = new LightstepAdapter(config); 13 | const opts = { 14 | spanFilter: { 15 | minDurationMicroseconds: 100, 16 | } 17 | }; 18 | const resp = await fetch("test.c.instr.wasm"); 19 | 20 | const bytes = await resp.arrayBuffer(); 21 | const traceContext = await adapter.start(bytes, opts); 22 | 23 | let fds = [ 24 | new OpenFile( 25 | new File( 26 | new TextEncoder("utf-8").encode(`count these vowels for me please`), 27 | ), 28 | ), // stdin 29 | new OpenFile(new File([])), // stdout 30 | new OpenFile(new File([])), // stderr 31 | ]; 32 | let wasi = new WASI([], [], fds); 33 | const instance = await WebAssembly.instantiate(bytes, { 34 | "wasi_snapshot_preview1": wasi.wasiImport, 35 | ...traceContext.getImportObject(), 36 | }); 37 | 38 | wasi.start(instance.instance); 39 | let utf8decoder = new TextDecoder(); 40 | console.log(utf8decoder.decode(fds[1].file.data)); 41 | traceContext.stop(); 42 | }; 43 | 44 | f().then(() => { }); 45 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.3.1" 13 | } 14 | }, 15 | "../..": { 16 | "name": "@dylibso/observe-sdk-honeycomb", 17 | "version": "1.0.0", 18 | "extraneous": true, 19 | "license": "ISC" 20 | }, 21 | "node_modules/dotenv": { 22 | "version": "16.3.1", 23 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 24 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 25 | "engines": { 26 | "node": ">=12" 27 | }, 28 | "funding": { 29 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.3.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/web/test.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-lightstep/test/web/test.c.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dylibso-observe-sdk-lightstep", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev" 9 | }, 10 | "devDependencies": { 11 | "@cloudflare/workers-types": "^4.20231218.0", 12 | "typescript": "^5.0.4", 13 | "wrangler": "^3.22.2" 14 | }, 15 | "dependencies": { 16 | "@bjorn3/browser_wasi_shim": "^0.2.17", 17 | "@dylibso/observe-sdk-lightstep": "file:../.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/src/code.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-lightstep/test/workerd/src/code.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/src/index.ts: -------------------------------------------------------------------------------- 1 | import { LightstepAdapter, LightstepConfig } from "@dylibso/observe-sdk-lightstep"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | import code from './code.wasm'; 4 | 5 | export interface Env { 6 | HONEYCOMB_API_KEY: string; 7 | } 8 | 9 | export default { 10 | async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise { 11 | // setup some custom configuration for the adapter 12 | const config: LightstepConfig = { 13 | // loaded as a Secret defined in the Worker, 14 | // see: https://developers.cloudflare.com/workers/wrangler/configuration/#environmental-variables 15 | apiKey: env.LIGHTSTEP_API_KEY, 16 | serviceName: 'deno', 17 | emitTracesInterval: 1000, 18 | traceBatchMax: 100, 19 | host: 'https://ingest.lightstep.com', 20 | } 21 | 22 | // create a new instance of the adapter with the config, this should be shared across requests 23 | const adapter = new LightstepAdapter(config); 24 | 25 | // setup some files for stdin, stdout, and stderr 26 | let fds = [ 27 | new OpenFile( 28 | new File( 29 | new TextEncoder().encode(await req.text()), 30 | ), 31 | ), // stdin 32 | new OpenFile(new File([])), // stdout 33 | new OpenFile(new File([])), // stderr 34 | ]; 35 | 36 | // instantiate the wasm module 37 | let wasi = new WASI([], [], fds); 38 | 39 | // start the adapter with the wasm module bytes and options 40 | const traceContext = await adapter.start(code, {}); 41 | 42 | // create a new instance of the wasm module using a new trace context to record observability data 43 | const instance = await WebAssembly.instantiate(code, { 44 | "wasi_snapshot_preview1": wasi.wasiImport, 45 | ...traceContext.getImportObject(), 46 | }); 47 | 48 | // execute the module 49 | wasi.start(instance); 50 | let dec = new TextDecoder(); 51 | const output = dec.decode(fds[1].file.data); 52 | 53 | traceContext.stop(); 54 | await adapter.send(); 55 | 56 | return new Response(output) 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/test/workerd/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "dylibso-observe-sdk-lightstep" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-12-18" 4 | 5 | # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) 6 | # Note: Use secrets to store sensitive data. 7 | # Docs: https://developers.cloudflare.com/workers/platform/environment-variables 8 | # [vars] 9 | # MY_VARIABLE = "production_value" 10 | 11 | # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 12 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/kv 13 | # [[kv_namespaces]] 14 | # binding = "MY_KV_NAMESPACE" 15 | # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 16 | 17 | # Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. 18 | # Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ 19 | # [[r2_buckets]] 20 | # binding = "MY_BUCKET" 21 | # bucket_name = "my-bucket" 22 | 23 | # Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. 24 | # Docs: https://developers.cloudflare.com/queues/get-started 25 | # [[queues.producers]] 26 | # binding = "MY_QUEUE" 27 | # queue = "my-queue" 28 | 29 | # Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. 30 | # Docs: https://developers.cloudflare.com/queues/get-started 31 | # [[queues.consumers]] 32 | # queue = "my-queue" 33 | 34 | # Bind another Worker service. Use this binding to call another Worker without network overhead. 35 | # Docs: https://developers.cloudflare.com/workers/platform/services 36 | # [[services]] 37 | # binding = "MY_SERVICE" 38 | # service = "my-service" 39 | 40 | # Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. 41 | # Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. 42 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects 43 | # [[durable_objects.bindings]] 44 | # name = "MY_DURABLE_OBJECT" 45 | # class_name = "MyDurableObject" 46 | 47 | # Durable Object migrations. 48 | # Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations 49 | # [[migrations]] 50 | # tag = "v1" 51 | # new_classes = ["MyDurableObject"] 52 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-lightstep/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "index.ts", 4 | ], 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "dist/types", 10 | "outFile": "dist/types/index.d.ts", 11 | "declarationMap": true, 12 | "allowImportingTsExtensions": true, 13 | "lib": [ 14 | "DOM", 15 | "ESNext", 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .github/ 3 | .npmignore 4 | .travis.yml 5 | badge.png 6 | badge.svg 7 | CONTRIBUTING.md 8 | docs/ 9 | docs/logos/ 10 | SECURITY.md 11 | sticker.png 12 | sticker.svg 13 | test/ 14 | tmp/ 15 | tools/ 16 | *.code-workspace 17 | .env 18 | .vscode 19 | esbuild/ 20 | test-data/ -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | StdOutAdapter, 3 | } from '../../src/lib/adapters/stdout/mod'; 4 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-stdout", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@dylibso/observe-sdk-stdout", 9 | "version": "0.2.0", 10 | "license": "ISC" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dylibso/observe-sdk-stdout", 3 | "version": "0.2.0", 4 | "description": "A library that produces function tracing to stdout", 5 | "directories": { 6 | "test": "test" 7 | }, 8 | "main": "./dist/cjs/index.js", 9 | "module": "./dist/esm/index.js", 10 | "types": "./dist/types/index.d.ts", 11 | "exports": { 12 | "workerd": "./dist/workerd/index.js", 13 | "default": { 14 | "types": "./dist/types/index.d.ts", 15 | "import": "./dist/esm/index.js", 16 | "require": "./dist/cjs/index.js" 17 | } 18 | }, 19 | "scripts": { 20 | "build:esm": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/esm/index.js -p browser -f esm", 21 | "build:cjs": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/cjs/index.js -p browser -f cjs", 22 | "build:workerd": "node ../../esbuild/esbuild.js -b -e ./index.js -o ./dist/workerd/index.js -p browser -f esm --workerd", 23 | "build:types": "tsc -b", 24 | "build": "npm run build:esm && npm run build:cjs && npm run build:workerd && npm run build:types", 25 | "build:web-test": "node ../../esbuild/esbuild.js -b -e ./test/web/index.js -o ./test/web/build.js -p browser", 26 | "test:node": "node test/node/index.js", 27 | "test:deno": "deno run -A test/deno/index.ts", 28 | "test:web": "npm run build:web-test && npx serve ./test/web", 29 | "test:workerd": "npx wrangler dev -c ./test/workerd/wrangler.toml" 30 | }, 31 | "keywords": [], 32 | "author": "", 33 | "license": "ISC" 34 | } 35 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/deno/index.ts: -------------------------------------------------------------------------------- 1 | import { StdOutAdapter } from "../../dist/esm/index.js"; // TODO: test as Deno external dep (can't test with npm link) 2 | import Context from "https://deno.land/std@0.192.0/wasi/snapshot_preview1.ts"; 3 | 4 | const adapter = new StdOutAdapter(); 5 | const bytes = await Deno.readFile("../../test-data/test.c.instr.wasm"); 6 | const traceContext = await adapter.start(bytes, { 7 | spanFilter: { 8 | minDurationMicroseconds: 0, 9 | } 10 | }); 11 | const module = new WebAssembly.Module(bytes); 12 | 13 | const runtime = new Context({ 14 | stdin: Deno.stdin.rid, 15 | stdout: Deno.stdout.rid, 16 | }); 17 | const instance = new WebAssembly.Instance( 18 | module, 19 | { 20 | "wasi_snapshot_preview1": runtime.exports, 21 | ...traceContext.getImportObject(), 22 | }, 23 | ); 24 | runtime.start(instance); 25 | 26 | traceContext.stop(); 27 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/node/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const { WASI } = require("wasi"); 3 | const { StdOutAdapter } = require("@dylibso/observe-sdk-stdout"); 4 | const { env, argv } = require('node:process') 5 | 6 | const wasi = new WASI({ 7 | version: 'preview1', 8 | args: argv.slice(1), 9 | env, 10 | }); 11 | 12 | const adapter = new StdOutAdapter(); 13 | const bytes = fs.readFileSync("../../test-data/test.c.instr.wasm"); 14 | adapter.start(bytes, { 15 | spanFilter: { 16 | minDurationMicroseconds: 0, 17 | } 18 | }).then((traceContext) => { 19 | const module = new WebAssembly.Module(bytes); 20 | 21 | WebAssembly.instantiate(module, { 22 | ...wasi.getImportObject(), 23 | ...traceContext.getImportObject(), 24 | }).then((instance) => { 25 | wasi.start(instance); 26 | traceContext.stop(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "test", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-stdout": "file:../.." 13 | } 14 | }, 15 | "../..": { 16 | "version": "0.1.3", 17 | "license": "ISC" 18 | }, 19 | "node_modules/@dylibso/observe-sdk-stdout": { 20 | "resolved": "../..", 21 | "link": true 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@dylibso/observe-sdk-stdout": "file:../.." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/web/count_vowels.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-stdout/test/web/count_vowels.instr.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/web/index.js: -------------------------------------------------------------------------------- 1 | import { StdOutAdapter } from "@dylibso/observe-sdk-stdout"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | 4 | const f = async () => { 5 | const adapter = new StdOutAdapter(); 6 | const resp = await fetch("count_vowels.instr.wasm"); 7 | 8 | const bytes = await resp.arrayBuffer(); 9 | const traceContext = await adapter.start(bytes); 10 | 11 | let fds = [ 12 | new OpenFile( 13 | new File( 14 | new TextEncoder("utf-8").encode(`count these vowels for me please`), 15 | ), 16 | ), // stdin 17 | new OpenFile(new File([])), // stdout 18 | new OpenFile(new File([])), // stderr 19 | ]; 20 | let wasi = new WASI([], [], fds); 21 | const instance = await WebAssembly.instantiate(bytes, { 22 | "wasi_snapshot_preview1": wasi.wasiImport, 23 | ...traceContext.getImportObject(), 24 | }); 25 | 26 | wasi.start(instance.instance); 27 | let utf8decoder = new TextDecoder(); 28 | console.log(utf8decoder.decode(fds[1].file.data)); 29 | traceContext.stop(); 30 | }; 31 | 32 | f().then(() => { }); 33 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | tab_width = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.yml] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dylibso-observe-sdk-stdout", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "start": "wrangler dev" 9 | }, 10 | "devDependencies": { 11 | "@cloudflare/workers-types": "^4.20231218.0", 12 | "typescript": "^5.0.4", 13 | "wrangler": "^3.22.2" 14 | }, 15 | "dependencies": { 16 | "@bjorn3/browser_wasi_shim": "^0.2.17", 17 | "@dylibso/observe-sdk-stdout": "file:../.." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/src/code.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/packages/observe-sdk-stdout/test/workerd/src/code.wasm -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/src/index.ts: -------------------------------------------------------------------------------- 1 | import { StdOutAdapter } from "@dylibso/observe-sdk-stdout"; 2 | import { File, OpenFile, WASI } from "@bjorn3/browser_wasi_shim"; 3 | import code from './code.wasm'; 4 | 5 | export interface Env { 6 | HONEYCOMB_API_KEY: string; 7 | } 8 | 9 | export default { 10 | async fetch(req: Request, env: Env, _ctx: ExecutionContext): Promise { 11 | // create a new instance of the adapter with the config, this should be shared across requests 12 | const adapter = new StdOutAdapter(); 13 | 14 | console.log("yooo!!!") 15 | 16 | // setup some files for stdin, stdout, and stderr 17 | let fds = [ 18 | new OpenFile( 19 | new File( 20 | new TextEncoder().encode(await req.text()), 21 | ), 22 | ), // stdin 23 | new OpenFile(new File([])), // stdout 24 | new OpenFile(new File([])), // stderr 25 | ]; 26 | 27 | // instantiate the wasm module 28 | let wasi = new WASI([], [], fds); 29 | 30 | // start the adapter with the wasm module bytes and options 31 | const traceContext = await adapter.start(code, {}); 32 | 33 | // create a new instance of the wasm module using a new trace context to record observability data 34 | const instance = await WebAssembly.instantiate(code, { 35 | "wasi_snapshot_preview1": wasi.wasiImport, 36 | ...traceContext.getImportObject(), 37 | }); 38 | 39 | // execute the module 40 | wasi.start(instance); 41 | let dec = new TextDecoder(); 42 | const output = dec.decode(fds[1].file.data); 43 | 44 | traceContext.stop(); 45 | await adapter.send(); 46 | 47 | return new Response(output) 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/test/workerd/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "dylibso-observe-sdk-stdout" 2 | main = "src/index.ts" 3 | compatibility_date = "2023-12-18" 4 | 5 | # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) 6 | # Note: Use secrets to store sensitive data. 7 | # Docs: https://developers.cloudflare.com/workers/platform/environment-variables 8 | # [vars] 9 | # MY_VARIABLE = "production_value" 10 | 11 | # Bind a KV Namespace. Use KV as persistent storage for small key-value pairs. 12 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/kv 13 | # [[kv_namespaces]] 14 | # binding = "MY_KV_NAMESPACE" 15 | # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 16 | 17 | # Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files. 18 | # Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/ 19 | # [[r2_buckets]] 20 | # binding = "MY_BUCKET" 21 | # bucket_name = "my-bucket" 22 | 23 | # Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer. 24 | # Docs: https://developers.cloudflare.com/queues/get-started 25 | # [[queues.producers]] 26 | # binding = "MY_QUEUE" 27 | # queue = "my-queue" 28 | 29 | # Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them. 30 | # Docs: https://developers.cloudflare.com/queues/get-started 31 | # [[queues.consumers]] 32 | # queue = "my-queue" 33 | 34 | # Bind another Worker service. Use this binding to call another Worker without network overhead. 35 | # Docs: https://developers.cloudflare.com/workers/platform/services 36 | # [[services]] 37 | # binding = "MY_SERVICE" 38 | # service = "my-service" 39 | 40 | # Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model. 41 | # Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps. 42 | # Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects 43 | # [[durable_objects.bindings]] 44 | # name = "MY_DURABLE_OBJECT" 45 | # class_name = "MyDurableObject" 46 | 47 | # Durable Object migrations. 48 | # Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations 49 | # [[migrations]] 50 | # tag = "v1" 51 | # new_classes = ["MyDurableObject"] 52 | -------------------------------------------------------------------------------- /js/packages/observe-sdk-stdout/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "index.ts", 4 | ], 5 | "compilerOptions": { 6 | "allowJs": true, 7 | "declaration": true, 8 | "emitDeclarationOnly": true, 9 | "outDir": "dist/types", 10 | "outFile": "dist/types/index.d.ts", 11 | "declarationMap": true, 12 | "allowImportingTsExtensions": true, 13 | "lib": [ 14 | "DOM", 15 | "ESNext", 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /js/src/lib/adapters/datadog/formatter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Nanoseconds, 3 | MemoryGrowAmount, 4 | newSpanId, 5 | newTraceId, 6 | TelemetryId, 7 | } from "../../mod.ts"; 8 | import { DatadogMetadata } from "./mod.ts"; 9 | 10 | export interface Span { 11 | trace_id: number; 12 | span_id: number; 13 | parent_id?: number; 14 | name: string; 15 | start: number; 16 | duration: number; 17 | resource: string; 18 | error: number; 19 | meta: Map; 20 | metrics: Map; 21 | service: string; 22 | type?: string; 23 | } 24 | 25 | export class Trace { 26 | meta?: DatadogMetadata; 27 | spans: Span[]; 28 | trace_id: TelemetryId; 29 | 30 | constructor() { 31 | this.spans = []; 32 | this.trace_id = newTraceId(); 33 | } 34 | 35 | toJSON(): Span[] { 36 | return this.spans; 37 | } 38 | } 39 | 40 | const allocationKey = 'allocation'; 41 | 42 | export const addAllocation = (span: Span, amount: MemoryGrowAmount) => { 43 | let sumAmount = amount; 44 | let existingAllocation = span.meta[allocationKey]; 45 | if (existingAllocation) { 46 | try { 47 | sumAmount = parseInt(existingAllocation) + amount; 48 | } catch (e) { 49 | console.error(e); 50 | } 51 | } 52 | span.meta[allocationKey] = sumAmount.toString(); 53 | }; 54 | 55 | export class DatadogFormatter { 56 | constructor(public traces: Trace[]) { } 57 | 58 | public addTrace(trace: Trace) { 59 | this.traces.push(trace); 60 | } 61 | 62 | private truncateName(name: string, max: number) { 63 | const snip = `[...]`; 64 | if (name.length > max) { 65 | return `${name.slice(0, max)}${snip}`; 66 | } 67 | 68 | return name; 69 | } 70 | 71 | public newSpan( 72 | serviceName: string, 73 | traceId: number, 74 | name: string, 75 | start: Nanoseconds, 76 | duration: Nanoseconds, 77 | parentId?: number, 78 | ): Span { 79 | return { 80 | trace_id: traceId, 81 | span_id: newSpanId(), 82 | name: name, 83 | meta: new Map(), 84 | metrics: new Map(), 85 | start, 86 | duration, 87 | resource: name, 88 | service: serviceName, 89 | error: 0, 90 | parent_id: parentId, 91 | }; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /js/src/lib/adapters/honeycomb/mod.ts: -------------------------------------------------------------------------------- 1 | import { Adapter, ObserveEvent, Options, WASM } from "../../mod.ts"; 2 | import { SpanCollector } from "../../collectors/span/mod.ts"; 3 | import { traceFromEvents, Trace, TracesData } from "../../formatters/opentelemetry.ts"; 4 | import { AdapterConfig } from "../../../lib/mod.ts"; 5 | 6 | const defaultConfig: HoneycombConfig = { 7 | apiKey: '', 8 | dataset: 'default-dataset', 9 | emitTracesInterval: 1000, 10 | traceBatchMax: 100, 11 | host: 'https://api.honeycomb.io', 12 | } 13 | 14 | export interface HoneycombConfig extends AdapterConfig { 15 | apiKey: string; 16 | dataset: string; 17 | traceBatchMax: number; 18 | host: string, 19 | } 20 | 21 | export class HoneycombAdapter extends Adapter { 22 | config: HoneycombConfig = defaultConfig; 23 | traces: Trace[] = []; 24 | 25 | constructor(config?: HoneycombConfig) { 26 | super(); 27 | if (config) { 28 | this.config = config; 29 | } 30 | } 31 | 32 | public async start(wasm: WASM, opts?: Options): Promise { 33 | super.startTraceInterval(); 34 | const collector = new SpanCollector(this, opts); 35 | await collector.setNames(wasm); 36 | return collector; 37 | } 38 | 39 | public collect(events: ObserveEvent[]): void { 40 | this.traces.push(traceFromEvents(this.config.dataset, events)); 41 | if (this.traces.length > this.config.traceBatchMax) { 42 | this.send(); 43 | this.restartTraceInterval(); 44 | } 45 | } 46 | 47 | private tracesEndpoint() { 48 | const endpoint = new URL(this.config.host); 49 | endpoint.pathname = `/v1/traces`; 50 | return endpoint; 51 | } 52 | 53 | async send() { 54 | this.traces.forEach(async (trace) => { 55 | const controller = new AbortController(); 56 | const id = setTimeout(() => controller.abort(), 1000); 57 | const bytes = TracesData.encode(trace).finish(); 58 | try { 59 | const resp = await fetch(this.tracesEndpoint(), { 60 | headers: { 61 | "content-type": "application/protobuf", 62 | "x-honeycomb-team": this.config.apiKey, 63 | }, 64 | method: "POST", 65 | body: bytes, 66 | signal: controller.signal, 67 | }); 68 | if (!resp.ok) { 69 | const msg = await resp.json(); 70 | console.error( 71 | "Request to honeycomb failed with status:", 72 | resp.status, 73 | msg 74 | ); 75 | } 76 | } catch (e) { 77 | console.error("Request to honeycomb failed:", e); 78 | } finally { 79 | clearTimeout(id); 80 | } 81 | }); 82 | this.traces = []; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /js/src/lib/adapters/lightstep/mod.ts: -------------------------------------------------------------------------------- 1 | import { Adapter, ObserveEvent, Options, WASM } from "../../mod.ts"; 2 | import { SpanCollector } from "../../collectors/span/mod.ts"; 3 | import { traceFromEvents, Trace, TracesData } from "../../formatters/opentelemetry.ts"; 4 | import { AdapterConfig } from "../../mod.ts"; 5 | 6 | const defaultConfig: LightstepConfig = { 7 | apiKey: '', 8 | serviceName: 'default-service-name', 9 | emitTracesInterval: 1000, 10 | traceBatchMax: 100, 11 | host: 'https://ingest.lightstep.com', 12 | } 13 | 14 | export interface LightstepConfig extends AdapterConfig { 15 | apiKey: string; 16 | serviceName: string; 17 | traceBatchMax: number; 18 | host: string, 19 | } 20 | 21 | export class LightstepAdapter extends Adapter { 22 | config: LightstepConfig = defaultConfig; 23 | traces: Trace[] = []; 24 | 25 | constructor(config?: LightstepConfig) { 26 | super(); 27 | if (config) { 28 | this.config = config; 29 | } 30 | } 31 | 32 | public async start(wasm: WASM, opts?: Options): Promise { 33 | super.startTraceInterval(); 34 | const collector = new SpanCollector(this, opts); 35 | await collector.setNames(wasm); 36 | return collector; 37 | } 38 | 39 | public collect(events: ObserveEvent[]): void { 40 | this.traces.push(traceFromEvents(this.config.serviceName, events)); 41 | if (this.traces.length > this.config.traceBatchMax) { 42 | this.send(); 43 | this.restartTraceInterval(); 44 | } 45 | } 46 | 47 | private tracesEndpoint() { 48 | const endpoint = new URL(this.config.host); 49 | endpoint.pathname = `/traces/otlp/v0.9`; 50 | return endpoint; 51 | } 52 | 53 | async send() { 54 | this.traces.forEach(async (trace) => { 55 | const controller = new AbortController(); 56 | const id = setTimeout(() => controller.abort(), 1000); 57 | const bytes = TracesData.encode(trace).finish(); 58 | try { 59 | const resp = await fetch(this.tracesEndpoint(), { 60 | headers: { 61 | "content-type": "application/x-protobuf", 62 | "lightstep-access-token": this.config.apiKey, 63 | }, 64 | method: "POST", 65 | body: bytes, 66 | signal: controller.signal, 67 | }); 68 | if (!resp.ok) { 69 | const msg = await resp.text(); 70 | console.error( 71 | "Request to lightstep failed with status:", 72 | resp.status, 73 | msg, 74 | ); 75 | } 76 | } catch (e) { 77 | console.error("Request to lightstep failed:", e); 78 | } finally { 79 | clearTimeout(id); 80 | } 81 | }); 82 | this.traces = []; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /js/src/lib/adapters/stdout/mod.ts: -------------------------------------------------------------------------------- 1 | import { Adapter, FunctionCall, MemoryGrow, ObserveEvent, Options, WASM } from "../../mod.ts"; 2 | import { SpanCollector } from "../../collectors/span/mod.ts"; 3 | 4 | export class StdOutAdapter extends Adapter { 5 | public async start(wasm: WASM, opts?: Options): Promise { 6 | const collector = new SpanCollector(this, opts); 7 | await collector.setNames(wasm); 8 | return collector; 9 | } 10 | 11 | public collect(events: ObserveEvent[]): void { 12 | events.forEach((ev) => printEvents(ev, 0)); 13 | } 14 | 15 | async send() { } 16 | } 17 | 18 | function printEvents(event: ObserveEvent, indentation: number) { 19 | if (event instanceof FunctionCall) { 20 | console.log( 21 | `${" ".repeat(indentation) 22 | } Call to ${event.name} took ${event.duration()}ns`, 23 | ); 24 | event.within.forEach((f) => { 25 | printEvents(f, indentation + 1); 26 | }); 27 | } 28 | if (event instanceof MemoryGrow) { 29 | console.log( 30 | `${" ".repeat(indentation - 1) 31 | } Allocation grew memory by ${event.getPages()} pages`, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /js/src/lib/collectors/span/modsurfer-demangle/modsurfer_demangle.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {string} name 5 | * @returns {string} 6 | */ 7 | export function demangle(name: string): string; 8 | -------------------------------------------------------------------------------- /js/src/lib/collectors/span/modsurfer-demangle/modsurfer_demangle.js: -------------------------------------------------------------------------------- 1 | import * as wasm from "./modsurfer_demangle_bg.wasm"; 2 | import { __wbg_set_wasm } from "./modsurfer_demangle_bg.js"; 3 | __wbg_set_wasm(wasm); 4 | export * from "./modsurfer_demangle_bg.js"; 5 | -------------------------------------------------------------------------------- /js/src/lib/collectors/span/modsurfer-demangle/modsurfer_demangle_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/src/lib/collectors/span/modsurfer-demangle/modsurfer_demangle_bg.wasm -------------------------------------------------------------------------------- /js/src/lib/collectors/span/modsurfer-demangle/modsurfer_demangle_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function demangle(a: number, b: number, c: number): void; 5 | export function __wbindgen_add_to_stack_pointer(a: number): number; 6 | export function __wbindgen_malloc(a: number, b: number): number; 7 | export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; 8 | export function __wbindgen_free(a: number, b: number, c: number): void; 9 | -------------------------------------------------------------------------------- /js/src/lib/collectors/span/modsurfer-demangle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modsurfer-demangle", 3 | "version": "0.1.0", 4 | "files": [ 5 | "modsurfer_demangle_bg.wasm", 6 | "modsurfer_demangle.js", 7 | "modsurfer_demangle_bg.js", 8 | "modsurfer_demangle.d.ts" 9 | ], 10 | "module": "modsurfer_demangle.js", 11 | "types": "modsurfer_demangle.d.ts", 12 | "sideEffects": [ 13 | "./modsurfer_demangle.js", 14 | "./snippets/*" 15 | ] 16 | } -------------------------------------------------------------------------------- /js/src/lib/parser/mod.ts: -------------------------------------------------------------------------------- 1 | // Parse the custom name section of the WASM module according to this spec: 2 | // https://webassembly.github.io/spec/core/appendix/custom.html#custom-sections 3 | export function parseNameSection(nameSection: ArrayBuffer): Map { 4 | const nameSectionView = new DataView(nameSection); 5 | const fnNameMap = new Map; 6 | let offset = 0; 7 | try { 8 | while (offset < nameSection.byteLength) { 9 | const subsectionId = readByte(nameSectionView, offset); 10 | offset += subsectionId.bytesRead; 11 | 12 | const subsectionLength = readLEB128(nameSectionView, offset); 13 | offset += subsectionLength.bytesRead; 14 | const subsectionEnd = offset + subsectionLength.value; 15 | 16 | // function name subsection is subsection id 1 17 | if (subsectionId.value === 0x01) { 18 | const nameMapLength = readLEB128(nameSectionView, offset); 19 | offset += nameMapLength.bytesRead; 20 | 21 | // process namemap 22 | for (let nameMapCount = 0; nameMapCount < nameMapLength.value; nameMapCount++) { 23 | const nameIdx = readLEB128(nameSectionView, offset); 24 | offset += nameIdx.bytesRead; 25 | 26 | const nameLength = readLEB128(nameSectionView, offset); 27 | offset += nameLength.bytesRead; 28 | 29 | const fnName = new TextDecoder().decode(nameSection.slice(offset, offset + nameLength.value)); 30 | offset += nameLength.value; 31 | 32 | fnNameMap.set(nameIdx.value, fnName); 33 | } 34 | if (offset < subsectionEnd) { 35 | console.warn("suspicious: at end of name map, but not its name subsection"); 36 | offset = subsectionEnd; 37 | } 38 | } else { 39 | // skip this subsection 40 | offset = subsectionEnd; 41 | } 42 | } 43 | } catch (error) { 44 | // WASM probably has a corrupt name section, log the error and return what we got 45 | console.error(error); 46 | } 47 | return fnNameMap; 48 | } 49 | 50 | function readLEB128(view: DataView, offset: number): { value: number, bytesRead: number } { 51 | let result = 0; 52 | let bytesRead = 0; 53 | let shift = 0; 54 | let byte; 55 | 56 | do { 57 | byte = view.getUint8(offset + bytesRead); 58 | result |= (byte & 0x7F) << shift; 59 | shift += 7; 60 | bytesRead++; 61 | } while (byte & 0x80); 62 | return { value: result, bytesRead }; 63 | } 64 | 65 | function readByte(view: DataView, offset: number): { value: number, bytesRead: number } { 66 | const byte = view.getUint8(offset); 67 | return { value: byte, bytesRead: 1 }; 68 | } 69 | -------------------------------------------------------------------------------- /js/src/sdk/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DatadogAdapter, 3 | type DatadogConfig, 4 | DefaultDatadogConfig, 5 | } from "../lib/adapters/datadog/mod.ts"; 6 | 7 | import { 8 | StdOutAdapter, 9 | } from "../lib/adapters/stdout/mod.ts"; 10 | 11 | export { 12 | DatadogAdapter, 13 | type DatadogConfig, 14 | DefaultDatadogConfig, 15 | StdOutAdapter, 16 | } 17 | -------------------------------------------------------------------------------- /js/test-data/rust_fib_instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/test-data/rust_fib_instr.wasm -------------------------------------------------------------------------------- /js/test-data/test.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/js/test-data/test.c.instr.wasm -------------------------------------------------------------------------------- /js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "allowImportingTsExtensions": true, 5 | "lib": [ 6 | "ESNext", 7 | "DOM" 8 | ], 9 | "declaration": true 10 | }, 11 | } -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | _help: 2 | @just --list 3 | 4 | _edit: 5 | @$EDITOR {{justfile()}} 6 | 7 | local_instr instr_path="../wasm-instr/wasm-instr": 8 | for i in test/*.c.wasm; do o=${i%.wasm}; {{ instr_path }} $i > $o.instr.wasm; done 9 | -------------------------------------------------------------------------------- /observe-api/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 2 3 | -------------------------------------------------------------------------------- /observe-api/Makefile: -------------------------------------------------------------------------------- 1 | WASICC?=$(WASI_SDK_PATH)/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot 2 | WASICXX?=$(WASI_SDK_PATH)/bin/clang++ --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot 3 | 4 | build: 5 | @cd test/rust && cargo build --target=wasm32-wasi && cd ../.. 6 | @cp test/rust/target/wasm32-wasi/debug/rust_guest.wasm test/rust_guest.wasm 7 | $(WASICC) -o test/c_guest.wasm -I c test/c/main.c 8 | $(WASICXX) -o test/cxx_guest.wasm -I c -x c++ test/c/main.c 9 | $(WASICXX) -o test/cxx_guest_2.wasm -fno-exceptions -I cxx test/cxx/main.cpp 10 | $(WASICXX) -o test/cxx_guest_3.wasm -fno-exceptions -I cxx test/cxx/main2.cpp -------------------------------------------------------------------------------- /observe-api/README.md: -------------------------------------------------------------------------------- 1 | # Observe API 2 | 3 | The *Observe API* is a set of *host* functions that are supported by each of our Host SDKs. 4 | This acts as the contract between the host and the guest layer. All data flows in one direction, 5 | from the guest to the host. Most of these APIs are simply ways to pass observability data as strings 6 | to the host layer. 7 | 8 | * `dylibso:observe/api.metric(i32, i32, i32)` 9 | * `dylibso:observe/api.log(i32, i32, i32)` 10 | * `dylibso:observe/api.span-enter(i32, i32)` 11 | * `dylibso:observe/api.span-exit()` 12 | * `dylibso:observe/api.span-tags(i32, i32)` 13 | 14 | Ideally, you will not call this API layer directly but instead use language specific bindings to call them. And for end users, eventually, open source observability clients will *export* data to this layer. 15 | 16 | ## Language Bindings 17 | 18 | We currently provide these language bindings to this API: 19 | 20 | ### [rust](rust/) 21 | 22 | * [example](test/rust/src/main.rs) 23 | 24 | 25 | ### [c](c/) and [c++](cxx/) 26 | 27 | Both the C and C++ bindings are implemented as single header libraries. To use the C bindings, 28 | in __ONE__ source file: 29 | 30 | ```c 31 | #define OBSERVE_API_IMPLEMENTATION 32 | #include "observe_api.h" 33 | ``` 34 | 35 | In other source files, just `#include "observe_api.h"` 36 | 37 | * [example](test/c/main.c) 38 | 39 | To use the C++ bindings, instead, in __ONE__ source file: 40 | 41 | ```c++ 42 | #define OBSERVE_API_CPP_IMPLEMENTATION 43 | #include "observe_api.hpp" 44 | ``` 45 | 46 | In other source files, just `#include "observe_api.hpp"` 47 | 48 | * [functional example](test/cxx/main.cpp) 49 | * [OO example](test/cxx/main2.cpp) 50 | 51 | In C++, both bindings may be used at the same time without conflict. 52 | 53 | ### Other 54 | 55 | More languages will come soon as well as tools built on top of these bindings. If you are planning on building your own tooling we suggest using or contributing one of these language specific bindings. 56 | 57 | -------------------------------------------------------------------------------- /observe-api/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dylibso/observe-sdk/observe-api/go 2 | 3 | go 1.21.1 4 | -------------------------------------------------------------------------------- /observe-api/go/observe_api.go: -------------------------------------------------------------------------------- 1 | package observe_api 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "unsafe" 7 | ) 8 | 9 | //go:wasmimport dylibso:observe/api metric 10 | func metric(uint32, uint32, uint32) 11 | 12 | //go:wasmimport dylibso:observe/api log 13 | func log(uint32, uint32, uint32) 14 | 15 | //go:wasmimport dylibso:observe/api span-tags 16 | func span_tags(uint32, uint32) 17 | 18 | //go:wasmimport dylibso:observe/api span-enter 19 | func span_enter(uint32, uint32) 20 | 21 | //go:wasmimport dylibso:observe/api span-exit 22 | func span_exit() 23 | 24 | func stringPointer(s *string) uint32 { 25 | header := (*reflect.StringHeader)(unsafe.Pointer(s)) 26 | return uint32(header.Data) 27 | } 28 | 29 | type MetricFormat int 30 | 31 | const ( 32 | Statsd MetricFormat = 1 + iota 33 | ) 34 | 35 | type LogLevel int 36 | 37 | const ( 38 | Error LogLevel = 1 + iota 39 | Warn 40 | Info 41 | Debug 42 | Trace 43 | ) 44 | 45 | func Metric(format MetricFormat, m string) { 46 | ptr := stringPointer(&m) 47 | metric(uint32(format), ptr, uint32(len(m))) 48 | } 49 | 50 | func Log(level LogLevel, msg string) { 51 | ptr := stringPointer(&msg) 52 | log(uint32(level), ptr, uint32(len(msg))) 53 | } 54 | 55 | func SpanTags(tags []string) { 56 | s := strings.Join(tags[:], ",") 57 | ptr := stringPointer(&s) 58 | span_tags(ptr, uint32(len(s))) 59 | } 60 | 61 | type span struct { 62 | name string 63 | tags []string 64 | } 65 | 66 | func NewSpan(name string) span { 67 | ptr := stringPointer(&name) 68 | span_enter(ptr, uint32(len(name))) 69 | tags := []string{} 70 | return span{name, tags} 71 | } 72 | 73 | func (s span) End() { 74 | span_exit() 75 | } 76 | 77 | func (s span) AddTags(tags ...string) { 78 | SpanTags(tags) 79 | } 80 | -------------------------------------------------------------------------------- /observe-api/rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "log" 7 | version = "0.4.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 10 | 11 | [[package]] 12 | name = "observe_api" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "log", 16 | "observe_instrument", 17 | ] 18 | 19 | [[package]] 20 | name = "observe_instrument" 21 | version = "0.1.0" 22 | dependencies = [ 23 | "proc-macro2", 24 | "quote", 25 | "syn", 26 | ] 27 | 28 | [[package]] 29 | name = "proc-macro2" 30 | version = "1.0.66" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 33 | dependencies = [ 34 | "unicode-ident", 35 | ] 36 | 37 | [[package]] 38 | name = "quote" 39 | version = "1.0.32" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" 42 | dependencies = [ 43 | "proc-macro2", 44 | ] 45 | 46 | [[package]] 47 | name = "syn" 48 | version = "1.0.109" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 51 | dependencies = [ 52 | "proc-macro2", 53 | "quote", 54 | "unicode-ident", 55 | ] 56 | 57 | [[package]] 58 | name = "unicode-ident" 59 | version = "1.0.11" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 62 | -------------------------------------------------------------------------------- /observe-api/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "observe_api" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4.19" 10 | observe_instrument = { path = "crates/instrument" } 11 | -------------------------------------------------------------------------------- /observe-api/rust/crates/instrument/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "observe_instrument" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.66" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 19 | dependencies = [ 20 | "unicode-ident", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.32" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "1.0.109" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-ident", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-ident" 45 | version = "1.0.11" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 48 | -------------------------------------------------------------------------------- /observe-api/rust/crates/instrument/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "observe_instrument" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = { version = "1.0", features = ["full"] } 13 | quote = "1.0" 14 | proc-macro2 = "1.0" 15 | 16 | -------------------------------------------------------------------------------- /observe-api/rust/crates/instrument/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use quote::quote; 5 | use syn::{parse_macro_input, ItemFn}; 6 | 7 | #[proc_macro_attribute] 8 | pub fn instrument(_args: TokenStream, input: TokenStream) -> TokenStream { 9 | // Parse the input tokens into a syntax tree 10 | let mut function = parse_macro_input!(input as ItemFn); 11 | 12 | // Get the function's name 13 | let fname = &function.sig.ident; 14 | 15 | // The original function block 16 | let original_block = &function.block; 17 | 18 | // Construct a new block with our instrument calls and the original function body 19 | function.block = syn::parse2(quote!({ 20 | span_enter(stringify!(#fname)); 21 | let result = #original_block; 22 | span_exit(); 23 | result 24 | })) 25 | .unwrap(); 26 | 27 | // Return the new function 28 | TokenStream::from(quote!(#function)) 29 | } 30 | -------------------------------------------------------------------------------- /observe-api/rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use observe_instrument::instrument; 2 | 3 | #[link(wasm_import_module = "dylibso:observe/api")] 4 | extern "C" { 5 | #[link_name = "metric"] 6 | fn _metric(format: u32, ptr: u32, len: u32); 7 | #[link_name = "log"] 8 | fn _log(level: u32, ptr: u32, len: u32); 9 | #[link_name = "span-enter"] 10 | fn _span_enter(ptr: u32, len: u32); 11 | #[link_name = "span-tags"] 12 | fn _span_tags(ptr: u32, len: u32); 13 | #[link_name = "span-exit"] 14 | fn _span_exit(); 15 | } 16 | 17 | pub fn log(level: log::Level, message: &str) { 18 | let level = level as u32; 19 | let ptr = message.as_ptr(); 20 | let ptr = ptr as u32; 21 | let len = message.len() as u32; 22 | unsafe { _log(level, ptr, len) }; 23 | } 24 | 25 | pub enum MetricFormat { 26 | Statsd = 1, 27 | } 28 | 29 | pub fn metric(format: MetricFormat, message: &str) { 30 | let format = format as u32; 31 | let ptr = message.as_ptr(); 32 | let ptr = ptr as u32; 33 | let len = message.len() as u32; 34 | unsafe { _metric(format, ptr, len) }; 35 | } 36 | 37 | pub fn span_enter(name: &str) { 38 | let ptr = name.as_ptr(); 39 | let ptr = ptr as u32; 40 | let len = name.len() as u32; 41 | unsafe { _span_enter(ptr, len) }; 42 | } 43 | 44 | pub fn span_tags(tags: Vec<&str>) { 45 | let tags = tags.join(","); 46 | let ptr = tags.as_ptr(); 47 | let ptr = ptr as u32; 48 | let len = tags.len() as u32; 49 | unsafe { _span_tags(ptr, len) }; 50 | } 51 | 52 | pub fn span_exit() { 53 | unsafe { _span_exit() }; 54 | } 55 | -------------------------------------------------------------------------------- /observe-api/test/c/main.c: -------------------------------------------------------------------------------- 1 | #define OBSERVE_API_IMPLEMENTATION 2 | #include "observe_api.h" 3 | #include 4 | #include 5 | 6 | void run() { 7 | observe_api_span_enter("printf"); 8 | observe_api_statsd("ok:aaaaa"); 9 | observe_api_log(DO_LL_INFO, "bbbbb"); 10 | observe_api_span_tags("abbc:def,(another:tag"); 11 | const char *const tags[] = {"taga:one", "tagb:two"}; 12 | observe_api_span_tags_from_array(tags, sizeof(tags) / sizeof(tags[0])); 13 | printf("Hello from Wasm!\n"); 14 | observe_api_span_exit(); 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | observe_api_span_enter("run"); 19 | run(); 20 | observe_api_span_exit(); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /observe-api/test/c_guest.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/observe-api/test/c_guest.wasm -------------------------------------------------------------------------------- /observe-api/test/cxx/main.cpp: -------------------------------------------------------------------------------- 1 | #define OBSERVE_API_CPP_IMPLEMENTATION 2 | #include "observe_api.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void run() { 9 | observe_api::span_enter("printf"); 10 | observe_api::statsd("ok:aaaaa"); 11 | observe_api::log(DO_LL_INFO, "bbbbb"); 12 | observe_api::span_tags("abbc:def,(another:tag"); 13 | std::vector tags = {"taga:one", "tagb:two"}; 14 | observe_api::span_tags(tags); 15 | printf("Hello from Wasm!\n"); 16 | observe_api::span_exit(); 17 | } 18 | 19 | int main(int argc, char *argv[]) { 20 | observe_api::span_enter("run"); 21 | run(); 22 | observe_api::span_exit(); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /observe-api/test/cxx/main2.cpp: -------------------------------------------------------------------------------- 1 | #define OBSERVE_API_CPP_IMPLEMENTATION 2 | #include "observe_api.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void run() { 9 | auto span = observe_api::Span("printf"); 10 | span.statsd("ok:aaaaa"); 11 | observe_api::log(DO_LL_INFO, "bbbbb"); 12 | span.tags("abbc:def,(another:tag"); 13 | std::vector tags = {"taga:one", "tagb:two"}; 14 | span.tags(tags); 15 | printf("Hello from Wasm!\n"); 16 | } 17 | 18 | int main(int argc, char *argv[]) { 19 | auto span = observe_api::Span("run"); 20 | run(); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /observe-api/test/cxx_guest.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/observe-api/test/cxx_guest.wasm -------------------------------------------------------------------------------- /observe-api/test/cxx_guest_2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/observe-api/test/cxx_guest_2.wasm -------------------------------------------------------------------------------- /observe-api/test/cxx_guest_3.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/observe-api/test/cxx_guest_3.wasm -------------------------------------------------------------------------------- /observe-api/test/rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "log" 7 | version = "0.4.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" 10 | 11 | [[package]] 12 | name = "observe_api" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "log", 16 | "observe_instrument", 17 | ] 18 | 19 | [[package]] 20 | name = "observe_instrument" 21 | version = "0.1.0" 22 | dependencies = [ 23 | "proc-macro2", 24 | "quote", 25 | "syn", 26 | ] 27 | 28 | [[package]] 29 | name = "proc-macro2" 30 | version = "1.0.66" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 33 | dependencies = [ 34 | "unicode-ident", 35 | ] 36 | 37 | [[package]] 38 | name = "quote" 39 | version = "1.0.32" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" 42 | dependencies = [ 43 | "proc-macro2", 44 | ] 45 | 46 | [[package]] 47 | name = "rust_guest" 48 | version = "0.1.0" 49 | dependencies = [ 50 | "log", 51 | "observe_api", 52 | ] 53 | 54 | [[package]] 55 | name = "syn" 56 | version = "1.0.109" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 59 | dependencies = [ 60 | "proc-macro2", 61 | "quote", 62 | "unicode-ident", 63 | ] 64 | 65 | [[package]] 66 | name = "unicode-ident" 67 | version = "1.0.11" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 70 | -------------------------------------------------------------------------------- /observe-api/test/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4.19" 8 | observe_api = { path = "../../rust" } 9 | 10 | 11 | -------------------------------------------------------------------------------- /observe-api/test/rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use log::Level; 2 | use observe_api::{instrument, log, metric, span_enter, span_exit, span_tags, MetricFormat}; 3 | 4 | #[instrument] 5 | fn log_something(msg: &str) { 6 | metric(MetricFormat::Statsd, "worlds.helloed:1|c"); 7 | log(Level::Warn, msg); 8 | } 9 | 10 | #[instrument] 11 | fn run() { 12 | log_something("Hello World 1"); 13 | log_something("Hello World 2"); 14 | log_something("Hello World 3"); 15 | } 16 | 17 | #[instrument] 18 | fn main() { 19 | span_tags(vec!["user_id:123", "world:hello"]); 20 | run(); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /observe-api/test/rust_guest.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/observe-api/test/rust_guest.wasm -------------------------------------------------------------------------------- /proto/opentelemetry/proto/collector/README.md: -------------------------------------------------------------------------------- 1 | # OpenTelemetry Collector Proto 2 | 3 | This package describes the OpenTelemetry collector protocol. 4 | 5 | ## Packages 6 | 7 | 1. `common` package contains the common messages shared between different services. 8 | 2. `trace` package contains the Trace Service protos. 9 | 3. `metrics` package contains the Metrics Service protos. 10 | 4. `logs` package contains the Logs Service protos. 11 | -------------------------------------------------------------------------------- /proto/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.logs.v1.LogsService.Export 8 | post: /v1/logs 9 | body: "*" -------------------------------------------------------------------------------- /proto/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.metrics.v1.MetricsService.Export 8 | post: /v1/metrics 9 | body: "*" -------------------------------------------------------------------------------- /proto/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.trace.v1.TraceService.Export 8 | post: /v1/traces 9 | body: "*" 10 | -------------------------------------------------------------------------------- /proto/opentelemetry/proto/resource/v1/resource.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2019, OpenTelemetry Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package opentelemetry.proto.resource.v1; 18 | 19 | import "opentelemetry/proto/common/v1/common.proto"; 20 | 21 | option csharp_namespace = "OpenTelemetry.Proto.Resource.V1"; 22 | option java_multiple_files = true; 23 | option java_package = "io.opentelemetry.proto.resource.v1"; 24 | option java_outer_classname = "ResourceProto"; 25 | option go_package = "go.opentelemetry.io/proto/otlp/resource/v1"; 26 | 27 | // Resource information. 28 | message Resource { 29 | // Set of attributes that describe the resource. 30 | // Attribute keys MUST be unique (it is not allowed to have more than one 31 | // attribute with the same key). 32 | repeated opentelemetry.proto.common.v1.KeyValue attributes = 1; 33 | 34 | // dropped_attributes_count is the number of dropped attributes. If the value is 0, then 35 | // no attributes were dropped. 36 | uint32 dropped_attributes_count = 2; 37 | } 38 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dylibso-observe-sdk" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | # not testing docs because the prost generated structs fail 8 | doctest = false 9 | 10 | [dependencies] 11 | anyhow = "1" 12 | wasmtime = ">= 17.0.0, < 18.0.0" 13 | wasmtime-wasi = ">= 17.0.0, < 18.0.0" 14 | tokio = { version = "1", features = ["sync", "rt-multi-thread", "macros"] } 15 | rand = "0" 16 | modsurfer-demangle = { git = "https://github.com/dylibso/modsurfer" } 17 | log = "0.4" 18 | serde = { version = "1", features = ["derive"] } 19 | serde_json = "1" 20 | serde_tuple = "0" 21 | ureq = "2" 22 | url = "2" 23 | wasmparser = "0.107.0" 24 | prost = "0.11.9" 25 | env_logger = "0.10.0" 26 | 27 | [build-dependencies] 28 | prost-build = { version = "0.11.9" } 29 | -------------------------------------------------------------------------------- /rust/build.rs: -------------------------------------------------------------------------------- 1 | use std::io::Result; 2 | 3 | fn main() -> Result<()> { 4 | let mut prost_build = prost_build::Config::new(); 5 | prost_build.include_file("_includes.rs"); 6 | prost_build.compile_protos( 7 | &["../proto/opentelemetry/proto/trace/v1/trace.proto"], 8 | &["../proto/"], 9 | )?; 10 | Ok(()) 11 | } 12 | -------------------------------------------------------------------------------- /rust/examples/basic.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::stdout::StdoutAdapter; 2 | 3 | #[tokio::main] 4 | pub async fn main() -> anyhow::Result<()> { 5 | env_logger::init_from_env( 6 | env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "warn"), 7 | ); 8 | let args: Vec<_> = std::env::args().skip(1).collect(); 9 | let data = std::fs::read(&args[0])?; 10 | let function_name = "_start"; 11 | let config = wasmtime::Config::new(); 12 | 13 | // Create instance 14 | let engine = wasmtime::Engine::new(&config)?; 15 | let module = wasmtime::Module::new(&engine, &data)?; 16 | 17 | let adapter = StdoutAdapter::create(); 18 | 19 | // Setup WASI 20 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 21 | .inherit_env()? 22 | .inherit_stdio() 23 | .args(&args.clone())? 24 | .build(); 25 | 26 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 27 | let mut linker = wasmtime::Linker::new(&engine); 28 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 29 | 30 | // Provide the observability functions to the `Linker` to be made available 31 | // to the instrumented guest code. These are safe to add and are a no-op 32 | // if guest code is uninstrumented. 33 | let trace_ctx = adapter.start(&mut linker, &data, Default::default())?; 34 | 35 | let instance = linker.instantiate(&mut store, &module)?; 36 | 37 | // get the function and run it, the events pop into the queue 38 | // as the function is running 39 | 40 | let f = instance 41 | .get_func(&mut store, function_name) 42 | .expect("function exists"); 43 | f.call(&mut store, &[], &mut []).unwrap(); 44 | trace_ctx.shutdown().await; 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /rust/examples/datadog.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::datadog::{ 2 | AdapterMetadata, DatadogAdapter, DatadogConfig, DatadogMetadata, Options, SpanFilter, 3 | }; 4 | 5 | /// You need the datadog agent running on localhost for this example to work 6 | #[tokio::main] 7 | pub async fn main() -> anyhow::Result<()> { 8 | let args: Vec<_> = std::env::args().skip(1).collect(); 9 | let data = std::fs::read(&args[0])?; 10 | let function_name = "_start"; 11 | let config = wasmtime::Config::new(); 12 | 13 | // Create instance 14 | let engine = wasmtime::Engine::new(&config)?; 15 | let module = wasmtime::Module::new(&engine, &data)?; 16 | 17 | let ddconfig = DatadogConfig::default(); 18 | let adapter = DatadogAdapter::create(ddconfig); 19 | 20 | // Setup WASI 21 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 22 | .inherit_env()? 23 | .inherit_stdio() 24 | .args(&args.clone())? 25 | .build(); 26 | 27 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 28 | let mut linker = wasmtime::Linker::new(&engine); 29 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 30 | 31 | let options = Options { 32 | span_filter: SpanFilter { 33 | min_duration_microseconds: std::time::Duration::from_micros(100), 34 | }, 35 | }; 36 | // Provide the observability functions to the `Linker` to be made available 37 | // to the instrumented guest code. These are safe to add and are a no-op 38 | // if guest code is uninstrumented. 39 | let trace_ctx = adapter.start(&mut linker, &data, options)?; 40 | 41 | let instance = linker.instantiate(&mut store, &module)?; 42 | 43 | let f = instance 44 | .get_func(&mut store, function_name) 45 | .expect("function exists"); 46 | 47 | f.call(&mut store, &[], &mut []).unwrap(); 48 | 49 | let meta = DatadogMetadata { 50 | http_url: Some("https://example.com/things/123".into()), 51 | http_method: Some("GET".into()), 52 | http_status_code: Some(200u16), 53 | http_client_ip: Some("23.123.15.145".into()), 54 | http_request_content_length: Some(128974u64), 55 | http_response_content_length: Some(239823874u64), 56 | ..Default::default() 57 | }; 58 | 59 | trace_ctx.set_metadata(AdapterMetadata::Datadog(meta)).await; 60 | trace_ctx.shutdown().await; 61 | 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /rust/examples/honeycomb.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::honeycomb::{ 2 | AdapterMetadata, Attribute, HoneycombAdapter, HoneycombConfig, Options, SpanFilter, Value, 3 | }; 4 | 5 | #[tokio::main] 6 | pub async fn main() -> anyhow::Result<()> { 7 | let args: Vec<_> = std::env::args().skip(1).collect(); 8 | let data = std::fs::read(&args[0])?; 9 | let function_name = "_start"; 10 | let config = wasmtime::Config::new(); 11 | 12 | // Create instance 13 | let engine = wasmtime::Engine::new(&config)?; 14 | let module = wasmtime::Module::new(&engine, &data)?; 15 | 16 | let config = HoneycombConfig { 17 | api_key: String::from(std::env::var("HONEYCOMB_API_KEY")?), 18 | host: String::from("https://api.honeycomb.io"), 19 | dataset: String::from("rust"), 20 | }; 21 | let adapter = HoneycombAdapter::create(config); 22 | 23 | // Setup WASI 24 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 25 | .inherit_env()? 26 | .inherit_stdio() 27 | .args(&args.clone())? 28 | .build(); 29 | 30 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 31 | let mut linker = wasmtime::Linker::new(&engine); 32 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 33 | 34 | let options = Options { 35 | span_filter: SpanFilter { 36 | min_duration_microseconds: std::time::Duration::from_micros(20), 37 | }, 38 | }; 39 | // Provide the observability functions to the `Linker` to be made available 40 | // to the instrumented guest code. These are safe to add and are a no-op 41 | // if guest code is uninstrumented. 42 | let trace_ctx = adapter.start(&mut linker, &data, options)?; 43 | 44 | let instance = linker.instantiate(&mut store, &module)?; 45 | 46 | // get the function and run it, the events pop into the queue 47 | // as the function is running 48 | 49 | let f = instance 50 | .get_func(&mut store, function_name) 51 | .expect("function exists"); 52 | 53 | f.call(&mut store, &[], &mut []).unwrap(); 54 | 55 | let meta: Vec = vec![ 56 | Attribute { 57 | key: "http.url".into(), 58 | value: Value { 59 | string_value: Some("https://example.com/things/123".into()), 60 | int_value: None, 61 | }, 62 | }, 63 | Attribute { 64 | key: "http.client_ip".into(), 65 | value: Value { 66 | string_value: Some("23.123.15.145".into()), 67 | int_value: None, 68 | }, 69 | }, 70 | Attribute { 71 | key: "http.status_code".into(), 72 | value: Value { 73 | string_value: None, 74 | int_value: Some(200), 75 | }, 76 | }, 77 | ]; 78 | 79 | trace_ctx 80 | .set_metadata(AdapterMetadata::OpenTelemetry(meta)) 81 | .await; 82 | trace_ctx.shutdown().await; 83 | 84 | Ok(()) 85 | } 86 | -------------------------------------------------------------------------------- /rust/examples/lightstep.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::lightstep::{ 2 | AdapterMetadata, Attribute, LightstepAdapter, LightstepConfig, Options, SpanFilter, Value, 3 | }; 4 | 5 | #[tokio::main] 6 | pub async fn main() -> anyhow::Result<()> { 7 | let args: Vec<_> = std::env::args().skip(1).collect(); 8 | let data = std::fs::read(&args[0])?; 9 | let function_name = "_start"; 10 | let config = wasmtime::Config::new(); 11 | 12 | // Create instance 13 | let engine = wasmtime::Engine::new(&config)?; 14 | let module = wasmtime::Module::new(&engine, &data)?; 15 | 16 | let config = LightstepConfig { 17 | api_key: String::from(std::env::var("LIGHTSTEP_API_KEY")?), 18 | host: String::from("https://ingest.lightstep.com"), 19 | service_name: String::from("rust"), 20 | }; 21 | let adapter = LightstepAdapter::create(config); 22 | 23 | // Setup WASI 24 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 25 | .inherit_env()? 26 | .inherit_stdio() 27 | .args(&args.clone())? 28 | .build(); 29 | 30 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 31 | let mut linker = wasmtime::Linker::new(&engine); 32 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 33 | 34 | let options = Options { 35 | span_filter: SpanFilter { 36 | min_duration_microseconds: std::time::Duration::from_micros(0), 37 | }, 38 | }; 39 | // Provide the observability functions to the `Linker` to be made available 40 | // to the instrumented guest code. These are safe to add and are a no-op 41 | // if guest code is uninstrumented. 42 | let trace_ctx = adapter.start(&mut linker, &data, options)?; 43 | 44 | let instance = linker.instantiate(&mut store, &module)?; 45 | 46 | // get the function and run it, the events pop into the queue 47 | // as the function is running 48 | 49 | let f = instance 50 | .get_func(&mut store, function_name) 51 | .expect("function exists"); 52 | 53 | f.call(&mut store, &[], &mut []).unwrap(); 54 | 55 | let meta: Vec = vec![ 56 | Attribute { 57 | key: "http.url".into(), 58 | value: Value { 59 | string_value: Some("https://example.com/things/123".into()), 60 | int_value: None, 61 | }, 62 | }, 63 | Attribute { 64 | key: "http.client_ip".into(), 65 | value: Value { 66 | string_value: Some("23.123.15.145".into()), 67 | int_value: None, 68 | }, 69 | }, 70 | Attribute { 71 | key: "http.status_code".into(), 72 | value: Value { 73 | string_value: None, 74 | int_value: Some(200), 75 | }, 76 | }, 77 | ]; 78 | 79 | trace_ctx 80 | .set_metadata(AdapterMetadata::OpenTelemetry(meta)) 81 | .await; 82 | trace_ctx.shutdown().await; 83 | 84 | Ok(()) 85 | } 86 | -------------------------------------------------------------------------------- /rust/examples/many.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::{adapter::otelstdout::OtelStdoutAdapter, new_trace_id}; 2 | use rand::{seq::SliceRandom, thread_rng}; 3 | 4 | #[tokio::main] 5 | pub async fn main() -> anyhow::Result<()> { 6 | let args: Vec<_> = std::env::args().skip(1).collect(); 7 | let data = std::fs::read(&args[0])?; 8 | let function_name = "_start"; 9 | let config = wasmtime::Config::new(); 10 | 11 | // Create instance 12 | let engine = wasmtime::Engine::new(&config)?; 13 | let module = wasmtime::Module::new(&engine, &data)?; 14 | 15 | // create a thread-safe adapter container, which is used to create trace contexts, 16 | // one-per-instance of a wasm module. 17 | let adapter = OtelStdoutAdapter::create(); 18 | 19 | for _ in 0..5 { 20 | let mut instances = Vec::new(); 21 | for _ in 0..5 { 22 | // Setup WASI 23 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 24 | .inherit_env()? 25 | .inherit_stdio() 26 | .args(&args.clone())? 27 | .build(); 28 | 29 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 30 | let mut linker = wasmtime::Linker::new(&engine); 31 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 32 | 33 | // Provide the observability functions to the `Linker` to be made 34 | // available to the instrumented guest code. These are safe to add 35 | // and are a no-op if guest code is uninstrumented. 36 | let trace_ctx = adapter.start(&mut linker, &data, Default::default())?; 37 | 38 | let instance = linker.instantiate(&mut store, &module)?; 39 | instances.push((trace_ctx, instance, store)); 40 | } 41 | 42 | instances.shuffle(&mut thread_rng()); 43 | 44 | let mut tasks = vec![]; 45 | for (trace_ctx, instance, mut store) in instances { 46 | trace_ctx.set_trace_id(new_trace_id()).await; 47 | // get the function and run it, the events pop into the queue 48 | // as the function is running 49 | let t = tokio::spawn(async move { 50 | let f = instance 51 | .get_func(&mut store, function_name) 52 | .expect("function exists"); 53 | 54 | f.call(&mut store, &[], &mut []).unwrap(); 55 | 56 | trace_ctx.shutdown().await; 57 | }); 58 | tasks.push(t); 59 | } 60 | 61 | // we need to actually await the tasks to make sure they are done 62 | for t in tasks { 63 | t.await?; 64 | } 65 | } 66 | 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /rust/examples/otel-stdout.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::otelstdout::OtelStdoutAdapter; 2 | 3 | #[tokio::main] 4 | pub async fn main() -> anyhow::Result<()> { 5 | let args: Vec<_> = std::env::args().skip(1).collect(); 6 | let data = std::fs::read(&args[0])?; 7 | let function_name = "_start"; 8 | let config = wasmtime::Config::new(); 9 | 10 | // Create instance 11 | let engine = wasmtime::Engine::new(&config)?; 12 | let module = wasmtime::Module::new(&engine, &data)?; 13 | 14 | let adapter = OtelStdoutAdapter::create(); 15 | 16 | // Setup WASI 17 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 18 | .inherit_env()? 19 | .inherit_stdio() 20 | .args(&args.clone())? 21 | .build(); 22 | 23 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 24 | let mut linker = wasmtime::Linker::new(&engine); 25 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 26 | 27 | // Provide the observability functions to the `Linker` to be made available 28 | // to the instrumented guest code. These are safe to add and are a no-op 29 | // if guest code is uninstrumented. 30 | let trace_ctx = adapter.start(&mut linker, &data, Default::default())?; 31 | 32 | let instance = linker.instantiate(&mut store, &module)?; 33 | 34 | // get the function and run it, the events pop into the queue 35 | // as the function is running 36 | 37 | let f = instance 38 | .get_func(&mut store, function_name) 39 | .expect("function exists"); 40 | 41 | f.call(&mut store, &[], &mut []).unwrap(); 42 | 43 | trace_ctx.shutdown().await; 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /rust/examples/reactor-hello.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::stdout::StdoutAdapter; 2 | 3 | #[tokio::main] 4 | pub async fn main() -> anyhow::Result<()> { 5 | env_logger::init_from_env( 6 | env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "warn"), 7 | ); 8 | let args: Vec<_> = std::env::args().skip(1).collect(); 9 | let data = std::fs::read(&args[0])?; 10 | let function_name = "hello"; 11 | let config = wasmtime::Config::new(); 12 | 13 | // Create instance 14 | let engine = wasmtime::Engine::new(&config)?; 15 | let module = wasmtime::Module::new(&engine, &data)?; 16 | 17 | let adapter = StdoutAdapter::create(); 18 | 19 | // Setup WASI 20 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 21 | .inherit_env()? 22 | .inherit_stdio() 23 | .args(&args.clone())? 24 | .build(); 25 | 26 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 27 | let mut linker = wasmtime::Linker::new(&engine); 28 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 29 | 30 | // Provide the observability functions to the `Linker` to be made available 31 | // to the instrumented guest code. These are safe to add and are a no-op 32 | // if guest code is uninstrumented. 33 | let trace_ctx = adapter.start(&mut linker, &data, Default::default())?; 34 | 35 | let instance = linker.instantiate(&mut store, &module)?; 36 | 37 | // call _initialize" 38 | { 39 | let f = instance 40 | .get_func(&mut store, "_initialize") 41 | .expect("function exists"); 42 | f.call(&mut store, &[], &mut []).unwrap(); 43 | } 44 | 45 | // call hello 46 | let f = instance 47 | .get_func(&mut store, function_name) 48 | .expect("function exists 2"); 49 | f.call(&mut store, &[], &mut []).unwrap(); 50 | trace_ctx.shutdown().await; 51 | 52 | // call __wasm_call_dtors 53 | // Normally reactors don't emit __wasm_call_dtors, but 54 | // reactor-hello.c.instr.wasm includes it as an example of specifying 55 | // a cleanup function in a reactor. 56 | { 57 | let f = instance 58 | .get_func(&mut store, "__wasm_call_dtors") 59 | .expect("function exists"); 60 | f.call(&mut store, &[], &mut []).unwrap(); 61 | } 62 | 63 | Ok(()) 64 | } 65 | -------------------------------------------------------------------------------- /rust/examples/zipkin.rs: -------------------------------------------------------------------------------- 1 | use dylibso_observe_sdk::adapter::zipkin::ZipkinAdapter; 2 | 3 | #[tokio::main] 4 | pub async fn main() -> anyhow::Result<()> { 5 | let args: Vec<_> = std::env::args().skip(1).collect(); 6 | let data = std::fs::read(&args[0])?; 7 | let function_name = "_start"; 8 | let config = wasmtime::Config::new(); 9 | 10 | // Create instance 11 | let engine = wasmtime::Engine::new(&config)?; 12 | let module = wasmtime::Module::new(&engine, &data)?; 13 | 14 | let adapter = ZipkinAdapter::create(); 15 | 16 | // Setup WASI 17 | let wasi_ctx = wasmtime_wasi::WasiCtxBuilder::new() 18 | .inherit_env()? 19 | .inherit_stdio() 20 | .args(&args.clone())? 21 | .build(); 22 | 23 | let mut store = wasmtime::Store::new(&engine, wasi_ctx); 24 | let mut linker = wasmtime::Linker::new(&engine); 25 | wasmtime_wasi::add_to_linker(&mut linker, |wasi| wasi)?; 26 | 27 | // Provide the observability functions to the `Linker` to be made available 28 | // to the instrumented guest code. These are safe to add and are a no-op 29 | // if guest code is uninstrumented. 30 | let trace_ctx = adapter.start(&mut linker, &data, Default::default())?; 31 | 32 | let instance = linker.instantiate(&mut store, &module)?; 33 | 34 | // get the function and run it, the events pop into the queue 35 | // as the function is running 36 | 37 | let f = instance 38 | .get_func(&mut store, function_name) 39 | .expect("function exists"); 40 | 41 | f.call(&mut store, &[], &mut []).unwrap(); 42 | 43 | trace_ctx.shutdown().await; 44 | 45 | Ok(()) 46 | } 47 | -------------------------------------------------------------------------------- /rust/src/adapter/datadog_formatter/mod.rs: -------------------------------------------------------------------------------- 1 | use super::datadog::DatadogConfig; 2 | use crate::new_span_id; 3 | use anyhow::Result; 4 | use serde::Serialize; 5 | use std::{collections::HashMap, time::SystemTime}; 6 | 7 | #[derive(Default)] 8 | pub struct DatadogFormatter { 9 | pub traces: Vec, 10 | } 11 | 12 | pub type Trace = Vec; 13 | 14 | impl DatadogFormatter { 15 | pub fn new() -> DatadogFormatter { 16 | Default::default() 17 | } 18 | 19 | pub fn add_trace(&mut self, trace: Trace) { 20 | self.traces.push(trace) 21 | } 22 | } 23 | 24 | #[derive(Default, Debug, Clone, PartialEq, Serialize)] 25 | pub struct Span { 26 | pub trace_id: u64, 27 | pub span_id: u64, 28 | pub parent_id: Option, 29 | pub name: String, 30 | pub start: u64, 31 | pub duration: u64, 32 | pub resource: String, 33 | pub error: u8, 34 | pub meta: HashMap, 35 | pub metrics: HashMap, 36 | pub service: String, 37 | #[serde(rename = "type")] 38 | pub typ: Option, 39 | } 40 | 41 | impl Span { 42 | pub fn new( 43 | config: DatadogConfig, 44 | trace_id: u64, 45 | parent_id: Option, 46 | name: String, 47 | start_time: SystemTime, 48 | end_time: SystemTime, 49 | ) -> Result { 50 | let span_id = new_span_id().into(); 51 | 52 | Ok(Span { 53 | trace_id, 54 | span_id, 55 | parent_id, 56 | name: name.clone(), 57 | meta: HashMap::new(), 58 | metrics: HashMap::new(), 59 | service: config.service_name, 60 | start: start_time 61 | .duration_since(SystemTime::UNIX_EPOCH)? 62 | .as_nanos() as u64, 63 | duration: end_time.duration_since(start_time)?.as_nanos() as u64, 64 | resource: name, 65 | typ: None, 66 | error: 0, 67 | }) 68 | } 69 | 70 | pub fn add_allocation(&mut self, amount: u32) { 71 | let key = "allocation".to_string(); 72 | let mut amount = amount; 73 | if let Some(alloc) = self.meta.get(key.as_str()) { 74 | if let Ok(v) = alloc.parse::() { 75 | amount = v + amount; 76 | } 77 | } 78 | self.meta.insert(key, amount.to_string()); 79 | } 80 | 81 | pub fn add_tag(&mut self, tag: String) { 82 | // TODO need a more robust way to do this 83 | let parts: Vec<&str> = tag.split(|c| c == ':').collect(); 84 | let key = parts.get(0).unwrap(); 85 | let value = parts.get(1).unwrap(); 86 | self.meta.insert(key.to_string(), value.to_string()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /rust/src/adapter/honeycomb.rs: -------------------------------------------------------------------------------- 1 | use prost::Message; 2 | use std::time::Duration; 3 | 4 | use crate::TraceEvent; 5 | use anyhow::Result; 6 | 7 | use super::{otel_formatter::OtelFormatter, Adapter, AdapterHandle}; 8 | 9 | pub use super::{ 10 | otel_formatter::{Attribute, Value}, 11 | AdapterMetadata, Options, SpanFilter, 12 | }; 13 | 14 | /// Config options for HoneycombAdapter 15 | #[derive(Debug, Clone)] 16 | pub struct HoneycombConfig { 17 | pub api_key: String, 18 | pub host: String, 19 | pub dataset: String, 20 | } 21 | 22 | /// An adapter to send events from your module to honeycomb using OpenTelemetry json format. 23 | pub struct HoneycombAdapter { 24 | trace_events: Vec, 25 | config: HoneycombConfig, 26 | } 27 | 28 | impl Adapter for HoneycombAdapter { 29 | fn handle_trace_event(&mut self, trace_evt: TraceEvent) -> Result<()> { 30 | self.trace_events.push(trace_evt); 31 | self.dump_traces()?; 32 | Ok(()) 33 | } 34 | } 35 | 36 | impl HoneycombAdapter { 37 | /// Creates the Honeycomb adapter and spawns a task for it. 38 | /// This should ideally be created once per process of 39 | /// your rust application. 40 | pub fn create(config: HoneycombConfig) -> AdapterHandle { 41 | Self::spawn(Self { 42 | config, 43 | trace_events: vec![], 44 | }) 45 | } 46 | 47 | fn dump_traces(&mut self) -> Result<()> { 48 | let mut spans = vec![]; 49 | for te in &self.trace_events { 50 | let trace_id = te.telemetry_id.to_hex_16(); 51 | for span in &te.events { 52 | self.event_to_otel_spans( 53 | &mut spans, 54 | span.clone(), 55 | vec![], 56 | trace_id.clone(), 57 | &te.metadata, 58 | )?; 59 | } 60 | } 61 | let dataset = &self.config.dataset.clone(); 62 | let otf = OtelFormatter::new(spans, dataset.into()); 63 | 64 | let host = url::Url::parse(&self.config.host)?; 65 | let url = host.join("/v1/traces")?.to_string(); 66 | 67 | let response = ureq::post(&url) 68 | .timeout(Duration::from_secs(1)) 69 | .set("content-type", "application/protobuf") 70 | .set("x-honeycomb-team", &self.config.api_key) 71 | .send_bytes(&otf.traces_data.encode_to_vec()); 72 | 73 | match response { 74 | Ok(r) => { 75 | if r.status() != 200 { 76 | log::warn!("Request to honeycomb agent failed with status: {:#?}", r); 77 | } else { 78 | // clear the traces because we've successfully submitted them 79 | self.trace_events.clear(); 80 | } 81 | } 82 | Err(e) => { 83 | log::warn!("Request to honeycomb agent failed: {:#?}", e); 84 | } 85 | } 86 | 87 | Ok(()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rust/src/adapter/lightstep.rs: -------------------------------------------------------------------------------- 1 | use prost::Message; 2 | use std::time::Duration; 3 | 4 | use crate::TraceEvent; 5 | use anyhow::Result; 6 | 7 | use super::{otel_formatter::OtelFormatter, Adapter, AdapterHandle}; 8 | 9 | pub use super::{ 10 | otel_formatter::{Attribute, Value}, 11 | AdapterMetadata, Options, SpanFilter, 12 | }; 13 | 14 | /// Config options for LightstepAdapter 15 | #[derive(Debug, Clone)] 16 | pub struct LightstepConfig { 17 | pub api_key: String, 18 | pub host: String, 19 | pub service_name: String, 20 | } 21 | 22 | /// An adapter to send events from your module to lightstep using OpenTelemetry json format. 23 | pub struct LightstepAdapter { 24 | trace_events: Vec, 25 | config: LightstepConfig, 26 | } 27 | 28 | impl Adapter for LightstepAdapter { 29 | fn handle_trace_event(&mut self, trace_evt: TraceEvent) -> Result<()> { 30 | self.trace_events.push(trace_evt); 31 | self.dump_traces()?; 32 | Ok(()) 33 | } 34 | } 35 | 36 | impl LightstepAdapter { 37 | /// Creates the Lightstep adapter and spawns a task for it. 38 | /// This should ideally be created once per process of 39 | /// your rust application. 40 | pub fn create(config: LightstepConfig) -> AdapterHandle { 41 | Self::spawn(Self { 42 | config, 43 | trace_events: vec![], 44 | }) 45 | } 46 | 47 | fn dump_traces(&mut self) -> Result<()> { 48 | let mut spans = vec![]; 49 | for te in &self.trace_events { 50 | let trace_id = te.telemetry_id.to_hex_16(); 51 | for span in &te.events { 52 | self.event_to_otel_spans( 53 | &mut spans, 54 | span.clone(), 55 | vec![], 56 | trace_id.clone(), 57 | &te.metadata, 58 | )?; 59 | } 60 | } 61 | let service_name = &self.config.service_name.clone(); 62 | let otf = OtelFormatter::new(spans, service_name.into()); 63 | 64 | let host = url::Url::parse(&self.config.host)?; 65 | let url = host.join("traces/otlp/v0.9")?.to_string(); 66 | 67 | let response = ureq::post(&url) 68 | .timeout(Duration::from_secs(1)) 69 | .set("content-type", "application/x-protobuf") 70 | .set("lightstep-access-token", &self.config.api_key) 71 | .send_bytes(&otf.traces_data.encode_to_vec()); 72 | 73 | match response { 74 | Ok(r) => { 75 | if r.status() != 200 { 76 | log::warn!("Request to lightstep agent failed with status: {:#?}", r); 77 | } else { 78 | // clear the traces because we've successfully submitted them 79 | self.trace_events.clear(); 80 | } 81 | } 82 | Err(e) => { 83 | log::warn!("Request to lightstep agent failed: {:#?}", e); 84 | } 85 | } 86 | 87 | Ok(()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /rust/src/adapter/otelstdout.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use crate::TraceEvent; 4 | use anyhow::Result; 5 | 6 | use super::{otel_formatter::OtelFormatter, Adapter, AdapterHandle}; 7 | 8 | /// An adapter to send events from your module to stdout using OpenTelemetry json format. 9 | pub struct OtelStdoutAdapter {} 10 | 11 | impl Adapter for OtelStdoutAdapter { 12 | fn handle_trace_event(&mut self, trace_evt: TraceEvent) -> Result<()> { 13 | let mut spans = vec![]; 14 | let trace_id = trace_evt.telemetry_id.to_hex_16(); 15 | for span in trace_evt.events { 16 | self.event_to_otel_spans( 17 | &mut spans, 18 | span, 19 | vec![], 20 | trace_id.clone(), 21 | &trace_evt.metadata, 22 | )?; 23 | } 24 | if !spans.is_empty() { 25 | let otf = OtelFormatter::new(spans, "stdout".into()); 26 | println!("{:?}", &otf.traces_data); 27 | } 28 | Ok(()) 29 | } 30 | } 31 | 32 | impl OtelStdoutAdapter { 33 | /// Creates the Otel Stdout adapter and spawns a task for it. 34 | /// This should ideally be created once per process of 35 | /// your rust application. 36 | pub fn create() -> AdapterHandle { 37 | Self::spawn(Self {}) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rust/src/adapter/stdout.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | 3 | use crate::{Event, TraceEvent}; 4 | use anyhow::{bail, Context, Result}; 5 | 6 | use super::{Adapter, AdapterHandle}; 7 | 8 | /// An adapter to send events from your module to stdout in our own format. 9 | /// Mostly useful for debugging. 10 | pub struct StdoutAdapter {} 11 | 12 | impl Adapter for StdoutAdapter { 13 | fn handle_trace_event(&mut self, trace_evt: TraceEvent) -> Result<()> { 14 | let first = trace_evt 15 | .events 16 | .iter() 17 | .find(|&e| matches!(e, Event::Func(_f))) 18 | .context("Stdout adapter did not get any events")?; 19 | let start = match first { 20 | Event::Func(f) => f.start, 21 | _ => bail!("Stdout adapter is expecting at least one function"), 22 | }; 23 | for span in trace_evt.events { 24 | self.handle_event(span, start, 0 as usize)?; 25 | } 26 | Ok(()) 27 | } 28 | } 29 | 30 | impl StdoutAdapter { 31 | /// Creates the Otel Stdout adapter and spawns a task for it. 32 | /// This should ideally be created once per process of 33 | /// your rust application. 34 | pub fn create() -> AdapterHandle { 35 | Self::spawn(Self {}) 36 | } 37 | 38 | fn handle_event(&self, event: Event, start: SystemTime, depth: usize) -> Result<()> { 39 | match event { 40 | Event::Func(f) => { 41 | println!( 42 | "{}{:?} Func: {}", 43 | "\t".repeat(depth), 44 | f.start.duration_since(start), 45 | f.name.unwrap_or("unknown-name".to_string()), 46 | ); 47 | for e in f.within.iter() { 48 | self.handle_event(e.to_owned(), start, depth + 1)?; 49 | } 50 | } 51 | Event::Alloc(a) => { 52 | println!("{:?} Alloc: {}", a.ts.duration_since(start), a.amount); 53 | } 54 | _ => {} 55 | } 56 | Ok(()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /rust/src/adapter/zipkin_formatter/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use std::collections::HashMap; 3 | use std::time::SystemTime; 4 | 5 | use crate::new_span_id; 6 | 7 | #[derive(Default, Debug, Clone, PartialEq, Serialize)] 8 | #[serde(rename_all = "camelCase")] 9 | pub struct ZipkinFormatter { 10 | pub spans: Vec, 11 | } 12 | 13 | #[derive(Default, Debug, Clone, PartialEq, Serialize)] 14 | #[serde(rename_all = "camelCase")] 15 | pub struct LocalEndpoint { 16 | pub service_name: Option, 17 | } 18 | 19 | #[derive(Default, Debug, Clone, PartialEq, Serialize)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct Span { 22 | pub id: String, 23 | pub trace_id: String, 24 | pub parent_id: Option, 25 | pub name: String, 26 | pub kind: String, 27 | pub timestamp: u64, 28 | pub duration: u64, 29 | pub tags: HashMap, 30 | pub local_endpoint: Option, 31 | } 32 | 33 | impl ZipkinFormatter { 34 | pub fn new() -> ZipkinFormatter { 35 | ZipkinFormatter { spans: vec![] } 36 | } 37 | } 38 | 39 | impl Span { 40 | pub fn new( 41 | trace_id: String, 42 | parent_id: Option, 43 | name: String, 44 | start_time: SystemTime, 45 | end_time: SystemTime, 46 | ) -> Span { 47 | let id = new_span_id().to_hex_8(); 48 | 49 | Span { 50 | id, 51 | trace_id, 52 | parent_id, 53 | name, 54 | kind: "SERVER".to_string(), 55 | timestamp: start_time 56 | .duration_since(SystemTime::UNIX_EPOCH) 57 | .unwrap() 58 | .as_micros() as u64, 59 | duration: end_time.duration_since(start_time).unwrap().as_micros() as u64, 60 | tags: HashMap::new(), 61 | local_endpoint: None, 62 | } 63 | } 64 | 65 | pub fn add_tag_i64(&mut self, name: String, value: i64) { 66 | self.tags.insert(name, value.to_string()); 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use std::time::Duration; 73 | 74 | use super::*; 75 | 76 | #[test] 77 | fn build_span() { 78 | let start = SystemTime::now() 79 | .checked_sub(Duration::new(0, 1500)) 80 | .unwrap(); 81 | let end = SystemTime::now(); 82 | let name = "function-call-start".to_string(); 83 | 84 | let span = Span::new( 85 | "1234abcd".to_string(), 86 | Some("f00df0b0b0".to_string()), 87 | name.clone(), 88 | start, 89 | end, 90 | ); 91 | 92 | assert_eq!(span.name, name); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /rust/src/collector.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use log::warn; 3 | use tokio::sync::mpsc::{Receiver, Sender}; 4 | 5 | use crate::{adapter::{AdapterHandle, AdapterMetadata}, new_trace_id, Event, TelemetryId, TraceEvent}; 6 | 7 | pub type CollectorHandle = Sender; 8 | 9 | /// A collector is spawned for each Wasm module. It receives Events 10 | /// from the InsturmentationContext object in the background as the Wasm 11 | /// module is running. It collects Events until ti receives the Shutdown message, 12 | /// then it crafts a TraceEvent to send to the Adapter. 13 | /// 14 | /// ┌────────────────┐ ┌──────────────┐ ┌───────────────┐ 15 | /// │ InstrContext │ │ Collector │ │ │ 16 | /// │ │ │ │ │ Adapter │ 17 | /// │ │ │ │ │ │ 18 | /// ├────────────────┤ ├──────────────┤ │ │ 19 | /// │ collector_tx ├────────►│ collector_rx │ │ │ 20 | /// └────────────────┘ ├──────────────┤ ├───────────────┤ 21 | /// │ adapter_tx ├──────────────►│ adapter_rx │ 22 | /// └──────────────┘ └───────────────┘ 23 | pub struct Collector { 24 | events: Vec, 25 | adapter: AdapterHandle, 26 | telemetry_id: TelemetryId, 27 | metadata: Option, 28 | } 29 | 30 | impl Collector { 31 | fn new(adapter: AdapterHandle) -> Collector { 32 | Collector { 33 | adapter, 34 | events: Vec::new(), 35 | telemetry_id: new_trace_id(), 36 | metadata: None, 37 | } 38 | } 39 | 40 | /// Spawns the collector in it's own task given an rx channel 41 | /// It needs the Event receiver and an AdapterHandle to send the TraceEvent to 42 | pub fn start(mut events_rx: Receiver, adapter: AdapterHandle) { 43 | tokio::spawn(async move { 44 | let mut collector = Collector::new(adapter); 45 | while let Some(event) = events_rx.recv().await { 46 | if let Err(e) = collector.handle_event(event) { 47 | warn!("Collector error occurred while handling event {e}"); 48 | }; 49 | } 50 | }); 51 | } 52 | 53 | /// Events coming in from the InstrumentationContext 54 | /// Here we queue up events until we get the collector shutdown event 55 | fn handle_event(&mut self, event: Event) -> Result<()> { 56 | match event { 57 | Event::TraceId(id) => { 58 | self.telemetry_id = id; 59 | } 60 | Event::Func(func) => { 61 | self.events.push(Event::Func(func)); 62 | } 63 | Event::Shutdown => { 64 | let trace = TraceEvent { 65 | events: self.events.drain(..).collect(), 66 | telemetry_id: self.telemetry_id.clone(), 67 | metadata: self.metadata.clone(), 68 | }; 69 | self.adapter.try_send(trace)?; 70 | } 71 | Event::Metadata(meta) => { 72 | self.metadata = Some(meta); 73 | } 74 | _ => { 75 | self.events.push(event.clone()); 76 | } 77 | } 78 | Ok(()) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | use adapter::AdapterMetadata; 2 | use rand::Rng; 3 | use std::time::SystemTime; 4 | 5 | pub mod adapter; 6 | pub mod collector; 7 | pub mod context; 8 | pub mod wasm_instr; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct TelemetryId(u128); 12 | 13 | impl TelemetryId { 14 | /// format as 8-byte zero-prefixed hex string 15 | pub fn to_hex_8(&self) -> String { 16 | // 8 bytes is 16 chars 17 | format!("{:016x}", self.0 as u64) 18 | } 19 | /// format as 16-byte zero-prefixed hex string 20 | pub fn to_hex_16(&self) -> String { 21 | // 16 bytes is 32 chars 22 | format!("{:032x}", self.0) 23 | } 24 | } 25 | 26 | impl From for u64 { 27 | fn from(v: TelemetryId) -> Self { 28 | v.0 as u64 29 | } 30 | } 31 | 32 | impl From for u128 { 33 | fn from(v: TelemetryId) -> Self { 34 | v.0 35 | } 36 | } 37 | 38 | pub fn new_trace_id() -> TelemetryId { 39 | let x = rand::thread_rng().gen::(); 40 | TelemetryId(x) 41 | } 42 | 43 | pub fn new_span_id() -> TelemetryId { 44 | let x = rand::thread_rng().gen::(); 45 | TelemetryId(x) 46 | } 47 | 48 | #[derive(Debug, Clone)] 49 | pub enum Event { 50 | Func(FunctionCall), 51 | Alloc(Allocation), 52 | TraceId(TelemetryId), 53 | Metadata(AdapterMetadata), 54 | Tags(Tags), 55 | Metric(Metric), 56 | Log(Log), 57 | Shutdown, 58 | } 59 | 60 | #[derive(Debug, Clone)] 61 | pub struct TraceEvent { 62 | events: Vec, 63 | telemetry_id: TelemetryId, 64 | metadata: Option, 65 | } 66 | 67 | #[derive(Debug, Clone)] 68 | pub struct FunctionCall { 69 | pub name: Option, 70 | pub raw_name: Option, 71 | pub index: u32, 72 | pub start: SystemTime, 73 | pub end: SystemTime, 74 | pub within: Vec, 75 | } 76 | 77 | #[derive(Debug, Clone)] 78 | pub struct Allocation { 79 | pub ts: SystemTime, 80 | pub amount: u32, 81 | } 82 | 83 | #[derive(Debug, Clone)] 84 | pub enum MetricFormat { 85 | Statsd = 1, 86 | } 87 | 88 | #[derive(Debug, Clone)] 89 | pub struct Metric { 90 | pub ts: SystemTime, 91 | pub format: MetricFormat, 92 | pub message: String, 93 | pub trace_id: Option, 94 | } 95 | 96 | #[derive(Debug, Clone)] 97 | pub struct Tags { 98 | pub ts: SystemTime, 99 | pub tags: Vec, 100 | } 101 | 102 | #[derive(Debug, Clone)] 103 | pub struct Log { 104 | pub ts: SystemTime, 105 | pub level: log::Level, 106 | pub message: String, 107 | } 108 | -------------------------------------------------------------------------------- /rust/src/wasm_instr.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use anyhow::{bail, Result}; 4 | 5 | // these control the versions of instrumented wasm supported 6 | // WASM_INSTR_VERSION_MAJOR must match the instrumented wasm 7 | // wasmInstrVersionMinor must be <= the value in instrumented wasm 8 | pub const WASM_INSTR_VERSION_MAJOR: u32 = 0; 9 | pub const WASM_INSTR_VERSION_MINOR: u32 = 4; // TODO: bump this to match compiler when ready 10 | 11 | // Static info from instrumentation 12 | pub struct WasmInstrInfo { 13 | pub function_names: HashMap, 14 | } 15 | 16 | impl WasmInstrInfo { 17 | pub fn new(data: &[u8]) -> Result { 18 | let mut function_names = HashMap::new(); 19 | let mut warn_on_dylibso_observe = true; 20 | let parser = wasmparser::Parser::new(0); 21 | for payload in parser.parse_all(data) { 22 | match payload? { 23 | wasmparser::Payload::CustomSection(custom) => { 24 | if custom.name() == "name" { 25 | let name_reader = 26 | wasmparser::NameSectionReader::new(custom.data(), custom.data_offset()); 27 | for x in name_reader.into_iter() { 28 | if let wasmparser::Name::Function(f) = x? { 29 | for k in f.into_iter() { 30 | let k = k?; 31 | function_names.insert(k.index, k.name.to_string()); 32 | } 33 | } 34 | } 35 | continue; 36 | } 37 | } 38 | wasmparser::Payload::ImportSection(importsec) => { 39 | for import in importsec.into_iter() { 40 | let import = import?; 41 | if import.module == "dylibso_observe" { 42 | if warn_on_dylibso_observe { 43 | warn_on_dylibso_observe = false; 44 | log::warn!( 45 | "Module uses deprecated namespace \"dylibso_observe\"! 46 | Please rebuild your module using the updated Observe API or reinstrument with the new version of wasm-instr." 47 | ); 48 | } 49 | for fname in ["span_enter", "span_tags", "metric", "log", "span_exit"] { 50 | if import.name == fname { 51 | bail!( 52 | "Module uses old version of Observe API! 53 | Please rebuild your module using the updated Observe API." 54 | ); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | _ => (), 61 | } 62 | } 63 | 64 | return Ok(Self { function_names }); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rust/tests/README.md: -------------------------------------------------------------------------------- 1 | # Integration Tests 2 | 3 | These integration tests run the examples from `rust/examples/` using the modules from `test/`. The test runner uses `std::process::Command`, captures stdout, and parses that to verify the activity of the example. This means the integration tests are tightly coupled to the examples! 4 | 5 | If it becomes burdensome to change the examples then the entire example main functions should be brought into their respective test. This will require a different manner for grabbing the output of the adapters, which itself introduces differences in the test. -------------------------------------------------------------------------------- /rust/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::process::Command; 4 | 5 | use anyhow::Result; 6 | 7 | #[test] 8 | fn integration_basic() -> Result<()> { 9 | // cargo run --example basic ../test/test.c.instr.wasm 10 | let output = Command::new("cargo") 11 | .args(&["run", "--example", "basic", "../test/test.c.instr.wasm"]) 12 | .output() 13 | .expect("Failed to run the example `examples/basic`"); 14 | 15 | let output = String::from_utf8(output.stdout)?; 16 | let output_lines = output.lines(); 17 | 18 | // First test that the expected output was printed 10 times 19 | assert_eq!( 20 | output_lines 21 | .clone() 22 | .filter(|l| l.contains("Hello, world!")) 23 | .count(), 24 | 10 25 | ); 26 | 27 | // Check that printf was also called 10 times 28 | assert_eq!( 29 | output_lines 30 | .clone() 31 | .filter(|l| l.ends_with("Func: printf")) 32 | .count(), 33 | 10 34 | ); 35 | 36 | Ok(()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rust/tests/many_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use anyhow::Result; 4 | use std::process::Command; 5 | 6 | mod helpers; 7 | 8 | #[test] 9 | fn integration_many() -> Result<()> { 10 | // cargo run --example many ../test/test.c.instr.wasm 11 | let output = Command::new("cargo") 12 | .args(&["run", "--example", "many", "../test/test.c.instr.wasm"]) 13 | .output() 14 | .expect("Failed to run the example `examples/basic`"); 15 | 16 | let output = String::from_utf8(output.stdout)?; 17 | let output_lines = output.lines(); 18 | let hellos = output_lines 19 | .clone() 20 | .filter(|l| l.contains("Hello, world!")) 21 | .count(); 22 | 23 | // First test that the modules ran the expected number of times 24 | assert_eq!(hellos, 250); 25 | 26 | Ok(()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rust/tests/otel_stdout_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use anyhow::Result; 4 | use std::process::Command; 5 | 6 | #[test] 7 | fn otel_stdout() -> Result<()> { 8 | // cargo run --example many ../test/test.c.instr.wasm 9 | let output = Command::new("cargo") 10 | .args(&[ 11 | "run", 12 | "--example", 13 | "otel-stdout", 14 | "../test/test.c.instr.wasm", 15 | ]) 16 | .output() 17 | .expect("Failed to run the example `examples/otel-stdout`"); 18 | 19 | let output = String::from_utf8(output.stdout)?; 20 | let output_lines = output.lines(); 21 | let start_fns = output_lines 22 | .clone() 23 | .filter(|l| l.contains("_start")) 24 | .count(); 25 | 26 | // First test that the modules ran the expected number of times 27 | assert_eq!(start_fns, 1); 28 | 29 | Ok(()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rust/tests/tests/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | pub mod otel_json; 3 | -------------------------------------------------------------------------------- /rust/tests/tests/helpers/otel_json.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | 3 | pub fn attribute_of_first_span(trace: &Value, attribute: String) -> Option { 4 | if let Some(span) = spans(trace).first() { 5 | let value = span[attribute].as_str().unwrap_or_default(); 6 | return Some(value.to_string()); 7 | } 8 | None 9 | } 10 | 11 | pub fn ids_and_parent_span_ids(trace: &Value) -> (Vec, Vec) { 12 | let parent_ids = spans(trace) 13 | .iter() 14 | .map(|span| span["parentSpanId"].as_str().unwrap().to_string()) 15 | .collect(); 16 | let ids = spans(trace) 17 | .iter() 18 | .map(|span| span["spanId"].as_str().unwrap().to_string()) 19 | .collect(); 20 | (ids, parent_ids) 21 | } 22 | 23 | pub fn spans(trace: &Value) -> Vec { 24 | if let Some(resource_spans) = trace["resourceSpans"].as_array().unwrap().first() { 25 | if let Some(spans) = resource_spans["scopeSpans"].as_array().unwrap().first() { 26 | return spans["spans"].as_array().unwrap().to_vec(); 27 | } 28 | } 29 | vec![] 30 | } 31 | -------------------------------------------------------------------------------- /scripts/generate-otel-ts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # npm i -g ts-proto 4 | # requires protoc installed as well (brew install protobuf, apt install protobuf-compiler) 5 | 6 | # from project root: 7 | protoc --plugin=ts_proto proto/opentelemetry/proto/trace/v1/trace.proto -I proto --ts_proto_out=./js/proto 8 | -------------------------------------------------------------------------------- /test/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 2 3 | -------------------------------------------------------------------------------- /test/constructor-destructor.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) static void constructor(void) { 4 | printf("constructor\n"); 5 | } 6 | 7 | int main(void) {} 8 | 9 | __attribute__((destructor)) static void destructor(void) { 10 | printf("destructor\n"); 11 | } 12 | -------------------------------------------------------------------------------- /test/constructor-destructor.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/constructor-destructor.c.instr.wasm -------------------------------------------------------------------------------- /test/constructor-destructor.c.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/constructor-destructor.c.wasm -------------------------------------------------------------------------------- /test/kitchensink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define IMPORT(a, b) __attribute__((import_module(a), import_name(b))) 7 | 8 | IMPORT("dylibso:observe/api", "metric") 9 | extern void metric(uint32_t, uint32_t, uint32_t); 10 | IMPORT("dylibso:observe/api", "log") 11 | extern void log_write(uint32_t, uint32_t, uint32_t); 12 | IMPORT("dylibso:observe/api", "span-enter") 13 | extern void span_enter(uint32_t, uint32_t); 14 | IMPORT("dylibso:observe/api", "span-exit") extern void span_exit(); 15 | 16 | void custom_span_enter(const char name[]) { 17 | const uintptr_t ptr = (uintptr_t)name; 18 | const uint32_t uint32_ptr = ptr; 19 | const uint32_t uint32_length = strlen(name); 20 | 21 | span_enter(uint32_ptr, uint32_length); 22 | } 23 | 24 | void custom_span_exit() { span_exit(); } 25 | 26 | void write_stat() { 27 | static const char stat[] = "vowels.count:1|c"; 28 | 29 | const uintptr_t ptr = (uintptr_t)stat; 30 | const uint32_t uint32_ptr = ptr; 31 | const uint32_t uint32_length = strlen(stat); 32 | 33 | custom_span_enter("statsd"); 34 | metric(1, uint32_ptr, uint32_length); 35 | custom_span_exit(); 36 | } 37 | 38 | void write_log() { 39 | static const char stat[] = "Vowels Counted: 3\n"; 40 | 41 | const uintptr_t ptr = (uintptr_t)stat; 42 | const uint32_t uint32_ptr = ptr; 43 | const uint32_t uint32_length = strlen(stat); 44 | const uint32_t uint32_level = 1; 45 | 46 | custom_span_enter("log_write"); 47 | log_write(uint32_level, uint32_ptr, uint32_length); 48 | custom_span_exit(); 49 | } 50 | 51 | void run() { 52 | custom_span_enter("printf"); 53 | printf("Hello from Wasm!\n"); 54 | custom_span_exit(); 55 | 56 | custom_span_enter("write_stat"); 57 | write_stat(); 58 | custom_span_exit(); 59 | 60 | custom_span_enter("write_log"); 61 | write_log(); 62 | custom_span_exit(); 63 | } 64 | 65 | int main(int argc, char *argv[]) { 66 | custom_span_enter("run"); 67 | run(); 68 | custom_span_exit(); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /test/kitchensink.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/kitchensink.c.instr.wasm -------------------------------------------------------------------------------- /test/kitchensink.c.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/kitchensink.c.wasm -------------------------------------------------------------------------------- /test/nested.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void three() { 5 | printf("Hello from Wasm!\n"); 6 | } 7 | 8 | void two() { 9 | three(); 10 | } 11 | 12 | void one() { 13 | two(); 14 | } 15 | 16 | int main(int argc, char *argv[]) { 17 | one(); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/nested.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/nested.c.instr.wasm -------------------------------------------------------------------------------- /test/nested.c.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/nested.c.wasm -------------------------------------------------------------------------------- /test/reactor-hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define EXPORT_AS(name) __attribute__((export_name(name))) 3 | 4 | __attribute__((constructor)) static void constructor(void) { 5 | printf("constructor\n"); 6 | } 7 | 8 | EXPORT_AS("hello") void hello(void) { printf("hello world\n"); } 9 | 10 | EXPORT_AS("__wasm_call_dtors") void __wasm_call_dtors(void) { 11 | printf("real destructor\n"); 12 | } 13 | -------------------------------------------------------------------------------- /test/reactor-hello.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/reactor-hello.c.instr.wasm -------------------------------------------------------------------------------- /test/reactor-hello.c.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/reactor-hello.c.wasm -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | const char *name = "world"; 6 | if (argc > 1) { 7 | name = argv[1]; 8 | } 9 | 10 | void *memory[10]; 11 | for (int i = 0; i < 10; i++) { 12 | printf("Hello, %s!\n", name); 13 | memory[i] = malloc(65536 * 3); 14 | } 15 | 16 | for (int i = 0; i < 10; i++) { 17 | free(memory[i]); 18 | } 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /test/test.c.instr.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/test.c.instr.wasm -------------------------------------------------------------------------------- /test/test.c.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylibso/observe-sdk/9145d8ad07e1280000354f3210f0283a1ce49ccd/test/test.c.wasm --------------------------------------------------------------------------------