├── .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
--------------------------------------------------------------------------------