├── .gitignore
├── .busted
├── examples
├── client
│ ├── Dockerfile
│ ├── go.mod
│ └── main.go
└── server
│ ├── Dockerfile
│ ├── go.mod
│ └── main.go
├── benchmark
├── run.sh
└── id_generator.lua
├── lib
└── opentelemetry
│ ├── instrumentation_library.lua
│ ├── trace
│ ├── span_status.lua
│ ├── span_kind.lua
│ ├── sampling
│ │ ├── always_on_sampler.lua
│ │ ├── always_off_sampler.lua
│ │ ├── result.lua
│ │ ├── parent_base_sampler.lua
│ │ └── trace_id_ratio_sampler.lua
│ ├── propagation
│ │ └── text_map
│ │ │ ├── getter.lua
│ │ │ ├── setter.lua
│ │ │ ├── noop_propagator.lua
│ │ │ ├── composite_propagator.lua
│ │ │ └── trace_context_propagator.lua
│ ├── id_generator.lua
│ ├── event.lua
│ ├── noop_span.lua
│ ├── non_recording_span.lua
│ ├── simple_span_processor.lua
│ ├── span_context.lua
│ ├── exporter
│ │ ├── console.lua
│ │ ├── http_client.lua
│ │ ├── encoder.lua
│ │ ├── circuit.lua
│ │ └── otlp.lua
│ ├── tracer.lua
│ ├── tracer_provider.lua
│ ├── recording_span.lua
│ └── tracestate.lua
│ ├── semantic_conventions
│ └── trace
│ │ ├── compatibility.lua
│ │ ├── trace.lua
│ │ ├── aws.lua
│ │ ├── exporter.lua
│ │ ├── feature.lua
│ │ ├── cloudevents.lua
│ │ ├── rpc.lua
│ │ ├── faas.lua
│ │ ├── database.lua
│ │ ├── http.lua
│ │ └── general.lua
│ ├── global.lua
│ ├── api
│ ├── README.md
│ └── trace
│ │ └── span_status.lua
│ ├── resource.lua
│ ├── metrics_reporter.lua
│ ├── attribute.lua
│ ├── baggage.lua
│ ├── baggage
│ └── propagation
│ │ └── text_map
│ │ └── baggage_propagator.lua
│ └── context.lua
├── utils
├── Dockerfile
└── generate_semantic_conventions.lua
├── spec
├── api
│ ├── trace
│ │ └── span_status_spec.lua
│ └── spec_helper.lua
├── trace
│ ├── span_context_spec.lua
│ ├── id_generator_spec.lua
│ ├── exporter
│ │ └── circuit_spec.lua
│ ├── tracestate_spec.lua
│ └── propagation
│ │ └── text_map
│ │ ├── trace_context_propagator_spec.lua
│ │ └── composite_propagator_spec.lua
├── baggage_spec.lua
└── context_spec.lua
├── e2e-trace-context.sh
├── Dockerfile
├── otel-collector-config.yaml
├── .lua-format
├── t
├── trace
│ ├── tracer_provider.t
│ ├── span_timestamps.t
│ ├── sampling
│ │ ├── trace_id_ratio_sampler.t
│ │ └── parent_base_sampler.t
│ └── tracer.t
└── context.t
├── busted-runner
├── docker-compose.yml
├── doc
├── modules
│ ├── opentelemetry.html
│ └── api.trace.span_status.html
└── index.html
├── CONTRIBUTING.md
├── Makefile
├── CHANGELOG.md
├── rockspec
├── opentelemetry-lua-0.1-0.rockspec
├── opentelemetry-lua-0.1-1.rockspec
├── opentelemetry-lua-0.1-2.rockspec
├── opentelemetry-lua-0.1-3.rockspec
├── opentelemetry-lua-0.2-0.rockspec
├── opentelemetry-lua-0.2-1.rockspec
├── opentelemetry-lua-0.2-2.rockspec
├── opentelemetry-lua-0.2-3.rockspec
├── opentelemetry-lua-0.2-4.rockspec
├── opentelemetry-lua-0.2-5.rockspec
└── opentelemetry-lua-0.2-6.rockspec
└── .github
└── workflows
└── ci.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vscode
3 | t/servroot
4 | .DS_STORE
5 |
--------------------------------------------------------------------------------
/.busted:
--------------------------------------------------------------------------------
1 | return {
2 | _all = {
3 | coverage = false
4 | },
5 | default = {
6 | verbose = true,
7 | pattern = "_spec"
8 | },
9 | }
10 |
--------------------------------------------------------------------------------
/examples/client/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.17
2 | COPY . /usr/src/client/
3 | WORKDIR /usr/src/client/
4 | RUN go env -w GOPROXY=direct
5 | RUN go install ./main.go
6 | CMD ["/go/bin/main"]
--------------------------------------------------------------------------------
/examples/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.17
2 | COPY . /usr/src/server/
3 | WORKDIR /usr/src/server/
4 | RUN go env -w GOPROXY=direct
5 | RUN go install ./main.go
6 | CMD ["/go/bin/main"]
--------------------------------------------------------------------------------
/benchmark/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | FILES="benchmark/*.lua"
4 | for f in $FILES
5 | do
6 | if [ -f "$f" ]
7 | then
8 | resty -I /opt/opentelemetry-lua/lib "$f"
9 | fi
10 | done
11 |
--------------------------------------------------------------------------------
/lib/opentelemetry/instrumentation_library.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | }
3 |
4 | function _M.new(name, version, schema_url)
5 | return {name=name, version=version, schema_url=schema_url}
6 | end
7 |
8 | return _M
--------------------------------------------------------------------------------
/benchmark/id_generator.lua:
--------------------------------------------------------------------------------
1 | local id_generator = require("lib.opentelemetry.trace.id_generator")
2 | local start = os.clock()
3 |
4 | for _ = 1, 5000000 do
5 | id_generator.new_ids()
6 | end
7 |
8 | print('5m new ids: ' .. (os.clock() - start) ..' seconds.')
9 |
--------------------------------------------------------------------------------
/utils/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openresty/openresty:1.21.4.1-0-centos
2 |
3 | RUN yum install -y gcc gcc-c++ git cmake
4 | RUN luarocks install busted 2.0.0-1
5 | RUN luarocks install ldoc 1.4.6-2
6 | RUN luarocks install --server=https://luarocks.org/dev luaformatter
7 |
8 | WORKDIR /opt/opentelemetry-lua
9 |
--------------------------------------------------------------------------------
/spec/api/trace/span_status_spec.lua:
--------------------------------------------------------------------------------
1 | local span_status = require("lib.opentelemetry.api.trace.span_status")
2 |
3 | describe("new()", function()
4 | it("defaults to unset", function()
5 | local s = span_status:new()
6 | assert.are_same(s.code, span_status.UNSET)
7 | end)
8 | end)
9 |
--------------------------------------------------------------------------------
/spec/api/spec_helper.lua:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------------------------------------------------
2 | -- Helps with api tests. Empty for now.
3 | -- @module spec_helper
4 | ------------------------------------------------------------------------------------------------------------------------
5 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/span_status.lua:
--------------------------------------------------------------------------------
1 | local api_span_status = require("opentelemetry.api.trace.span_status")
2 |
3 | local _M = api_span_status:new()
4 |
5 | -- returns a valid span status code
6 | function _M.validate(code)
7 | if not code or code < 0 or code > 2 then
8 | -- default unset
9 | return 0
10 | end
11 | return code
12 | end
13 |
14 | return _M
15 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/span_kind.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | unspecified = 0,
3 | internal = 1,
4 | server = 2,
5 | client = 3,
6 | producer = 4,
7 | consumer = 5,
8 | }
9 |
10 | -- returns a valid span kind value
11 | function _M.validate(kind)
12 | if not kind or kind < 0 or kind > 5 then
13 | -- default internal
14 | return 1
15 | end
16 | return kind
17 | end
18 |
19 | return _M
20 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/compatibility.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.compatibility
5 | local _M = {
6 | -- Parent-child Reference type
7 | OPENTRACING_REF_TYPE = "opentracing.ref_type"
8 | }
9 | return _M
10 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/sampling/always_on_sampler.lua:
--------------------------------------------------------------------------------
1 | local result_new = require("opentelemetry.trace.sampling.result").new
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | function _M.new()
11 | return setmetatable({}, mt)
12 | end
13 |
14 | function _M.should_sample(self, parameters)
15 | return result_new(2, parameters.parent_ctx:span_context().trace_state)
16 | end
17 |
18 | function _M.description(self)
19 | return "AlwaysOnSampler"
20 | end
21 |
22 | return _M
23 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/sampling/always_off_sampler.lua:
--------------------------------------------------------------------------------
1 | local result_new = require("opentelemetry.trace.sampling.result").new
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | function _M.new()
11 | return setmetatable({}, mt)
12 | end
13 |
14 | function _M.should_sample(self, parameters)
15 | return result_new(0, parameters.parent_ctx:span_context().trace_state)
16 | end
17 |
18 | function _M.description(self)
19 | return "AlwaysOffSampler"
20 | end
21 |
22 | return _M
23 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/trace.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.trace
5 | local _M = {
6 | -- SHOULD be set to true if the exception event is recorded at a point where it is known that the exception is escaping the scope of the span.
7 | EXCEPTION_ESCAPED = "exception.escaped"
8 | }
9 | return _M
10 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/aws.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.aws
5 | local _M = {
6 | -- The full invoked ARN as provided on the `Context` passed to the function (`Lambda-Runtime-Invoked-Function-Arn` header on the `/runtime/invocation/next` applicable).
7 | AWS_LAMBDA_INVOKED_ARN = "aws.lambda.invoked_arn"
8 | }
9 | return _M
10 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/propagation/text_map/getter.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | }
3 |
4 | local mt = {
5 | __index = _M
6 | }
7 |
8 | function _M.new()
9 | return setmetatable({}, mt)
10 | end
11 |
12 | ------------------------------------------------------------------
13 | -- Extract tracing header from nginx request
14 | --
15 | -- @param carrier (should be ngx.req)
16 | -- @param key HTTP header to get
17 | -- @return value of HTTP header
18 | ------------------------------------------------------------------
19 | function _M.get(carrier, key)
20 | return carrier.get_headers()[key]
21 | end
22 |
23 | return _M
24 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/exporter.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.exporter
5 | local _M = {
6 | -- Name of the code, either "OK" or "ERROR". MUST NOT be set if the status code is UNSET.
7 | OTEL_STATUS_CODE = "otel.status_code",
8 | -- Description of the Status if it has a value, otherwise not set.
9 | OTEL_STATUS_DESCRIPTION = "otel.status_description"
10 | }
11 | return _M
12 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/propagation/text_map/setter.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | }
3 |
4 | local mt = {
5 | __index = _M
6 | }
7 |
8 | function _M.new()
9 | return setmetatable({}, mt)
10 | end
11 |
12 | ------------------------------------------------------------------
13 | -- Add tracing information to nginx request as headers
14 | --
15 | -- @param carrier nginx request
16 | -- @param key HTTP header to set
17 | -- @param val value of HTTP header
18 | -- @return nil
19 | ------------------------------------------------------------------
20 | function _M.set(carrier, name, val)
21 | carrier.set_header(name, val)
22 | end
23 |
24 | return _M
25 |
--------------------------------------------------------------------------------
/spec/trace/span_context_spec.lua:
--------------------------------------------------------------------------------
1 | local span_context = require("opentelemetry.trace.span_context")
2 |
3 | describe("is_valid", function()
4 | it("returns false when traceid == 00000000000000000000000000000000", function()
5 | local sp_ctx = span_context.new("00000000000000000000000000000000", "1234567890123456", 1, nil, false)
6 | assert.is_not_true(sp_ctx:is_valid())
7 | end)
8 |
9 | it("returns false when spanid == 0000000000000000", function()
10 | local sp_ctx = span_context.new("00000000000000000000000000000001", "0000000000000000", 1, nil, false)
11 | assert.is_not_true(sp_ctx:is_valid())
12 | end)
13 | end)
14 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/id_generator.lua:
--------------------------------------------------------------------------------
1 | local util = require "opentelemetry.util"
2 | local bit = require 'bit'
3 |
4 | local tohex = bit.tohex
5 | local fmt = string.format
6 | local random = util.random
7 |
8 | local _M = {}
9 |
10 | function _M.new_span_id()
11 | return fmt("%s%s",
12 | tohex(random(0, 0xFFFFFFFF), 8),
13 | tohex(random(0, 0xFFFFFFFF), 8))
14 | end
15 |
16 | function _M.new_ids()
17 | return fmt("%s%s%s%s",
18 | tohex(random(0, 0xFFFFFFFF), 8),
19 | tohex(random(0, 0xFFFFFFFF), 8),
20 | tohex(random(0, 0xFFFFFFFF), 8),
21 | tohex(random(0, 0xFFFFFFFF), 8)), _M.new_span_id()
22 | end
23 |
24 | return _M
25 |
--------------------------------------------------------------------------------
/spec/trace/id_generator_spec.lua:
--------------------------------------------------------------------------------
1 | local id_generator = require("opentelemetry.trace.id_generator")
2 |
3 | describe("new_span_id", function()
4 | it("generates a 16 character hex string", function()
5 | for _ = 0, 100 do
6 | assert.is_equal(16, #id_generator.new_span_id())
7 | end
8 | end)
9 | end)
10 |
11 | describe("new_ids", function()
12 | it("generates a 16 character hex string and a 32 character string", function()
13 | for _ = 0, 100 do
14 | local trace_id, span_id = id_generator.new_ids()
15 | assert.is_equal(32, #trace_id)
16 | assert.is_equal(16, #span_id)
17 | end
18 | end)
19 | end)
20 |
--------------------------------------------------------------------------------
/e2e-trace-context.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ################################################################################
4 | # There's a test suite in the w3c/trace-context repo that we use against our
5 | # code and nginx.conf
6 | ################################################################################
7 |
8 | cd /opt
9 | if [ ! -d "trace-context" ]; then
10 | git clone https://github.com/w3c/trace-context
11 | fi
12 | cd trace-context/test
13 |
14 | # Run an individual test:
15 | # HARNESS_DEBUG=1 python3 test.py http://127.0.0.1:80/test/e2e/trace-context TraceContextTest.test_traceparent_parent_id_too_short
16 |
17 | # Run every test:
18 | python3 test.py http://127.0.0.1:80/test/e2e/trace-context
19 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openresty/openresty:1.21.4.1-0-centos
2 |
3 | RUN yum install -y gcc
4 | RUN yum -y --enablerepo=powertools install libyaml-devel libffi-devel
5 | RUN luarocks install lua-resty-http 0.16.1-0
6 | RUN luarocks install lua-protobuf 0.3.3
7 | RUN luarocks install net-url 1.1-1
8 | RUN luarocks install busted 2.0.0-1
9 | RUN luarocks --server=http://rocks.moonscript.org install lyaml
10 |
11 | RUN yum install -y cpanminus perl
12 | RUN cpanm --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
13 |
14 | RUN yum install -y python3 python3-devel git
15 | RUN pip3 install multidict attrs yarl async_timeout charset-normalizer idna_ssl aiosignal
16 | RUN pip3 install aiohttp
17 |
18 | WORKDIR /opt/opentelemetry-lua
19 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/event.lua:
--------------------------------------------------------------------------------
1 | local util = require("opentelemetry.util")
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | ------------------------------------------------------------------
11 | -- create a event.
12 | --
13 | -- @name event name
14 | -- @opts [optional]
15 | -- opts.attributes: a list of attribute
16 | -- @return event
17 | ------------------------------------------------------------------
18 | function _M.new(name, opts)
19 | local self = {
20 | name = name,
21 | attributes = opts.attributes,
22 | time_unix_nano = string.format("%d", util.time_nano())
23 | }
24 | return setmetatable(self, mt)
25 | end
26 |
27 | return _M
28 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/feature.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.feature
5 | local _M = {
6 | -- The unique identifier of the feature flag.
7 | FEATURE_FLAG_KEY = "feature_flag.key",
8 | -- The name of the service provider that performs the flag evaluation.
9 | FEATURE_FLAG_PROVIDER_NAME = "feature_flag.provider_name",
10 | -- SHOULD be a semantic identifier for a value. If one is unavailable, a stringified version of the value can be used.
11 | FEATURE_FLAG_VARIANT = "feature_flag.variant"
12 | }
13 | return _M
14 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/noop_span.lua:
--------------------------------------------------------------------------------
1 | local empty_span_context = require("opentelemetry.trace.span_context")
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | function _M.new()
11 | return setmetatable({ ctx = empty_span_context.new() }, mt)
12 | end
13 |
14 | function _M.context(self)
15 | return self.ctx
16 | end
17 |
18 | function _M.is_recording()
19 | return false
20 | end
21 |
22 | function _M.set_status()
23 | end
24 |
25 | function _M.set_attributes()
26 | end
27 |
28 | function _M.finish(_self, _end_timestamp)
29 | end
30 |
31 | function _M.record_error()
32 | end
33 |
34 | function _M.add_event()
35 | end
36 |
37 | function _M.set_name()
38 | end
39 |
40 | function _M.tracer_provider()
41 | end
42 |
43 | return _M
44 |
--------------------------------------------------------------------------------
/lib/opentelemetry/global.lua:
--------------------------------------------------------------------------------
1 | local metrics_reporter = require("opentelemetry.metrics_reporter")
2 |
3 | local _M = { context_storage = nil, metrics_reporter = metrics_reporter }
4 |
5 | function _M.set_tracer_provider(tp)
6 | _M.tracer_provider = tp
7 | end
8 |
9 | function _M.get_tracer_provider()
10 | return _M.tracer_provider
11 | end
12 |
13 | function _M.set_metrics_reporter(metrics_reporter)
14 | _M.metrics_reporter = metrics_reporter
15 | end
16 |
17 | function _M.tracer(name, opts)
18 | return _M.tracer_provider:tracer(name, opts)
19 | end
20 |
21 | function _M.get_context_storage()
22 | return _M.context_storage or ngx.ctx
23 | end
24 |
25 | function _M.set_context_storage(context_storage)
26 | _M.context_storage = context_storage
27 | end
28 |
29 | return _M
30 |
--------------------------------------------------------------------------------
/lib/opentelemetry/api/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | This folder contains an in-progress implementation of OpenTelemetry's API. All operations effectively no-op unless the program registers an SDK. For more on the API/SDK distinction, check out the [documentation](https://opentelemetry.io/docs/reference/specification/overview/#api).
4 |
5 | ## Dev dependencies
6 |
7 | `lua-formatter`, `busted`, `ldoc`.
8 |
9 | ## Running tests
10 |
11 | Run `make api-test` from root of repository.
12 |
13 | Run a single test by adding `#now` (or another tag of your choosing) to the test description `it("foo bar #now")` and then running `busted -m "./lib/?.lua;./lib/?/?.lua;./lib/?/?/?.lua" -t "now" spec/api`
14 |
15 | ## Generating docs
16 |
17 | `make doc`
18 |
19 | ## Formatting
20 |
21 | `make format` (we use [`lua-formatter`](https://github.com/Koihik/LuaFormatter) to format the code).
22 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/non_recording_span.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | }
3 |
4 | local mt = {
5 | __index = _M
6 | }
7 |
8 | function _M.new(tracer, ctx)
9 | local self = {
10 | tracer = tracer,
11 | ctx = ctx,
12 | }
13 | return setmetatable(self, mt)
14 | end
15 |
16 | function _M.context(self)
17 | return self.ctx
18 | end
19 |
20 | function _M.is_recording()
21 | return false
22 | end
23 |
24 | function _M.set_status()
25 | end
26 |
27 | function _M.set_attributes()
28 | end
29 |
30 | function _M.finish(_self, _end_timestamp)
31 | end
32 |
33 | function _M.record_error()
34 | end
35 |
36 | function _M.add_event()
37 | end
38 |
39 | function _M.set_name()
40 | end
41 |
42 | function _M.tracer_provider(self)
43 | return self.tracer.provider
44 | end
45 |
46 | function _M.plain(self)
47 | return {
48 | ctx = self.ctx
49 | }
50 | end
51 |
52 | return _M
53 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/sampling/result.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | drop = 0,
3 | record_only = 1,
4 | record_and_sample = 2
5 | }
6 |
7 | local mt = {
8 | __index = _M
9 | }
10 |
11 | ------------------------------------------------------------------
12 | -- create a sample result.
13 | --
14 | -- @decision 0: do nothing
15 | -- 1: recording
16 | -- 2: recording and sampling
17 | -- @return sample result
18 | ------------------------------------------------------------------
19 | function _M.new(decision, trace_state)
20 | return setmetatable({decision = decision, trace_state = trace_state}, mt)
21 | end
22 |
23 | function _M.is_sampled(self)
24 | return self.decision == self.record_and_sample
25 | end
26 |
27 | function _M.is_recording(self)
28 | return self.decision == self.record_only or self.decision == self.record_and_sample
29 | end
30 |
31 | return _M
32 |
--------------------------------------------------------------------------------
/otel-collector-config.yaml:
--------------------------------------------------------------------------------
1 | receivers:
2 | otlp:
3 | protocols:
4 | http:
5 | endpoint: 0.0.0.0:4317
6 |
7 | exporters:
8 | prometheus:
9 | endpoint: "0.0.0.0:8889"
10 | const_labels:
11 | label1: value1
12 | debug:
13 | verbosity: detailed
14 |
15 | #zipkin:
16 | # endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
17 | # format: proto
18 |
19 | otlp/jaeger:
20 | endpoint: jaeger:4317
21 | tls:
22 | insecure: true
23 |
24 | processors:
25 | batch:
26 |
27 | extensions:
28 | health_check:
29 | pprof:
30 | endpoint: :1888
31 | zpages:
32 | endpoint: :55679
33 |
34 | service:
35 | extensions: [pprof, zpages, health_check]
36 | pipelines:
37 | traces:
38 | receivers: [otlp]
39 | processors: [batch]
40 | exporters: [debug, otlp/jaeger]
41 | metrics:
42 | receivers: [otlp]
43 | processors: [batch]
44 | exporters: [debug, prometheus]
--------------------------------------------------------------------------------
/.lua-format:
--------------------------------------------------------------------------------
1 | column_limit: 120
2 | indent_width: 4
3 | use_tab: false
4 | tab_width: 4
5 | continuation_indent_width: 4
6 | spaces_before_call: 1
7 | keep_simple_control_block_one_line: false
8 | keep_simple_function_one_line: false
9 | align_args: true
10 | break_after_functioncall_lp: false
11 | break_before_functioncall_rp: false
12 | spaces_inside_functioncall_parens: false
13 | spaces_inside_functiondef_parens: false
14 | align_parameter: true
15 | chop_down_parameter: false
16 | break_after_functiondef_lp: false
17 | break_before_functiondef_rp: false
18 | align_table_field: true
19 | break_after_table_lb: true
20 | break_before_table_rb: true
21 | chop_down_table: false
22 | chop_down_kv_table: true
23 | table_sep: ","
24 | column_table_limit: 0
25 | extra_sep_at_table_end: false
26 | spaces_inside_table_braces: true
27 | break_after_operator: true
28 | double_quote_to_single_quote: false
29 | single_quote_to_double_quote: false
30 | spaces_around_equals_in_field: true
31 | line_breaks_after_function_body: 1
32 | line_separator: input
33 |
--------------------------------------------------------------------------------
/lib/opentelemetry/api/trace/span_status.lua:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------------------------------------------------
2 | -- Span status represents the status of a span. Like an HTTP code, but for your span. It is either unset, ok, or error.
3 | --
4 | -- @module api.trace.span_status
5 | ------------------------------------------------------------------------------------------------------------------------
6 | local _M = { UNSET = 0, OK = 1, ERROR = 2 }
7 |
8 | ------------------------------------------------------------------------------------------------------------------------
9 | -- Returns a new span_status.
10 | --
11 | -- @param[type=int] code The status code. Defaults to UNSET.
12 | ------------------------------------------------------------------------------------------------------------------------
13 | function _M:new(code)
14 | local ret = { code = code or _M.UNSET, description = nil }
15 | setmetatable(ret, { __index = self })
16 | self.__index = self
17 | return ret
18 | end
19 |
20 | return _M
21 |
--------------------------------------------------------------------------------
/t/trace/tracer_provider.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: tracer provider force_flush and shutdown
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local tracer_provider_new = require("opentelemetry.trace.tracer_provider").new
16 | local fake_processor = {
17 | force_flush = function()
18 | ngx.say("call span processor force_flush")
19 | end,
20 | shutdown = function()
21 | ngx.say("call span processor shutdown")
22 | end
23 | }
24 | local tracer_provider = tracer_provider_new(fake_processor)
25 | tracer_provider:force_flush()
26 | tracer_provider:shutdown()
27 | ngx.say("done")
28 | }
29 | }
30 | --- request
31 | GET /t
32 | --- error_code: 200
33 | --- response_body
34 | call span processor force_flush
35 | call span processor shutdown
36 | done
37 | --- no_error_log
38 | [error]
--------------------------------------------------------------------------------
/lib/opentelemetry/resource.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | }
3 |
4 | local mt = {
5 | __index = _M
6 | }
7 |
8 | ------------------------------------------------------------------
9 | -- create a resource.
10 | --
11 | -- @decision attribute1, attribute2, attribute3, ...
12 | -- @return resource
13 | ------------------------------------------------------------------
14 | function _M.new(...)
15 | local self = {
16 | attrs = {...}
17 | }
18 | return setmetatable(self, mt)
19 | end
20 |
21 | function _M.attributes(self)
22 | return self.attrs
23 | end
24 |
25 | function _M.merge(a, b)
26 | if a == nil then
27 | return b
28 | end
29 |
30 | local b_attr_keys = {}
31 | local new_attrs = {}
32 | for _, attr in ipairs(b.attrs) do
33 | table.insert(new_attrs, attr)
34 | b_attr_keys[attr.key] = true
35 | end
36 | for _, attr in ipairs(a.attrs) do
37 | if not b_attr_keys[attr.key] then
38 | table.insert(new_attrs, attr)
39 | end
40 | end
41 |
42 | return setmetatable({attrs = new_attrs}, mt)
43 | end
44 |
45 | return _M
--------------------------------------------------------------------------------
/examples/client/go.mod:
--------------------------------------------------------------------------------
1 | module client
2 |
3 | go 1.17
4 |
5 | require (
6 | go.opentelemetry.io/otel v1.3.0
7 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0
8 | go.opentelemetry.io/otel/sdk v1.3.0
9 | go.opentelemetry.io/otel/trace v1.3.0
10 | )
11 |
12 | require (
13 | github.com/cenkalti/backoff/v4 v4.1.2 // indirect
14 | github.com/go-logr/logr v1.2.1 // indirect
15 | github.com/go-logr/stdr v1.2.0 // indirect
16 | github.com/golang/protobuf v1.5.2 // indirect
17 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
18 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect
19 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect
20 | go.opentelemetry.io/proto/otlp v0.11.0 // indirect
21 | golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
22 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
23 | golang.org/x/text v0.3.0 // indirect
24 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
25 | google.golang.org/grpc v1.42.0 // indirect
26 | google.golang.org/protobuf v1.27.1 // indirect
27 | )
28 |
--------------------------------------------------------------------------------
/examples/server/go.mod:
--------------------------------------------------------------------------------
1 | module server
2 |
3 | go 1.17
4 |
5 | require (
6 | go.opentelemetry.io/otel v1.3.0
7 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0
8 | go.opentelemetry.io/otel/sdk v1.3.0
9 | go.opentelemetry.io/otel/trace v1.3.0
10 | )
11 |
12 | require (
13 | github.com/cenkalti/backoff/v4 v4.1.2 // indirect
14 | github.com/go-logr/logr v1.2.1 // indirect
15 | github.com/go-logr/stdr v1.2.0 // indirect
16 | github.com/golang/protobuf v1.5.2 // indirect
17 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
18 | go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0 // indirect
19 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0 // indirect
20 | go.opentelemetry.io/proto/otlp v0.11.0 // indirect
21 | golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
22 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
23 | golang.org/x/text v0.3.0 // indirect
24 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
25 | google.golang.org/grpc v1.42.0 // indirect
26 | google.golang.org/protobuf v1.27.1 // indirect
27 | )
28 |
--------------------------------------------------------------------------------
/busted-runner:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env resty
2 |
3 | package.path = './lib/?.lua;./lib/?/?.lua;./lib/?/init.lua' .. package.path
4 |
5 | -- Set up global tracer
6 | Global = require("opentelemetry.global")
7 | local tracer_provider = require("opentelemetry.trace.tracer_provider")
8 | local attr = require("opentelemetry.attribute")
9 | local resource = require("opentelemetry.resource")
10 | local always_on_sampler = require("opentelemetry.trace.sampling.always_on_sampler")
11 | local batch_span_processor = require("opentelemetry.trace.batch_span_processor")
12 | local exporter = require("opentelemetry.trace.exporter.console")
13 | local processor = batch_span_processor.new(exporter, {
14 | drop_on_queue_full = false, max_queue_size = 1024, batch_timeout = 3, inactive_timeout = 1, max_export_batch_size = 10
15 | })
16 | local tracer_provider = tracer_provider.new(processor, {
17 | sampler = always_on_sampler,
18 | resource = resource.new(attr.string("service.name", "openresty"), attr.int("attr_int", 100)),
19 | })
20 | Global.set_tracer_provider(tracer_provider)
21 |
22 | if ngx ~= nil then
23 | ngx.exit = function() end
24 | end
25 |
26 | -- Busted command-line runner
27 | require 'busted.runner' (
28 | {
29 | standalone = false,
30 | }
31 | )
32 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/simple_span_processor.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- The simple span processor immediately exports spans as they finish. It does
3 | -- not batch spans or process the spans in a background thread (as the batch
4 | -- span processor does via ngx.timer.at). It is intended to be used for
5 | -- debugging.
6 | --------------------------------------------------------------------------------
7 |
8 | local _M = {
9 | }
10 |
11 | local mt = {
12 | __index = _M
13 | }
14 |
15 | --------------------------------------------------------------------------------
16 | -- create a simple span processor.
17 | --
18 | -- @param exporter an exporter that will be used to send span data to its
19 | -- destination.
20 | -- @return processor
21 | --------------------------------------------------------------------------------
22 | function _M.new(exporter)
23 | return setmetatable({
24 | exporter = exporter,
25 | }, mt)
26 | end
27 |
28 | function _M.on_end(self, span)
29 | if not span.ctx:is_sampled() or self.closed then
30 | return
31 | end
32 |
33 | self.exporter:export_spans({ span })
34 | end
35 |
36 | function _M.shutdown(self)
37 | self.closed = true
38 | self.exporter:shutdown()
39 | end
40 |
41 | return _M
42 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | openresty:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile
6 | volumes:
7 | - ./examples/openresty/nginx.conf:/etc/nginx/conf.d/default.conf
8 | - .:/opt/opentelemetry-lua
9 | depends_on:
10 | - otel-collector
11 | - test-server
12 | networks:
13 | - opentelemetry-lua
14 | ports:
15 | - 80:80
16 | jaeger:
17 | image: jaegertracing/all-in-one:1
18 | ports:
19 | - 26686:16686
20 | networks:
21 | - opentelemetry-lua
22 | otel-collector:
23 | image: otel/opentelemetry-collector-contrib:0.98.0
24 | command: [ "--config=/etc/otel-collector-config.yaml" ]
25 | volumes:
26 | - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
27 | depends_on:
28 | - jaeger
29 | networks:
30 | - opentelemetry-lua
31 | test-server:
32 | build:
33 | context: ./examples/server
34 | depends_on:
35 | - otel-collector
36 | networks:
37 | - opentelemetry-lua
38 | test-client:
39 | build:
40 | context: ./examples/client
41 | environment:
42 | - PROXY_ENDPOINT=${PROXY_ENDPOINT}
43 | networks:
44 | - opentelemetry-lua
45 | utils:
46 | build:
47 | context: ./utils
48 | volumes:
49 | - .:/opt/opentelemetry-lua
50 | networks:
51 | opentelemetry-lua:
52 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- The noop propagator does nothing. It should be the default propagator for the
3 | -- API
4 | --------------------------------------------------------------------------------
5 | local _M = {
6 | }
7 |
8 | local mt = {
9 | __index = _M,
10 | }
11 |
12 | function _M.new()
13 | return setmetatable({}, mt)
14 | end
15 |
16 | --------------------------------------------------------------------------------
17 | -- noop injection
18 | --
19 | -- @param _context context storage
20 | -- @param _carrier nginx request
21 | -- @param _setter setter for interacting with carrier
22 | -- @return nil
23 | --------------------------------------------------------------------------------
24 | function _M:inject(_context, _carrier, _setter)
25 | end
26 |
27 | --------------------------------------------------------------------------------
28 | -- noop extraction
29 | --
30 | -- @param context context storage
31 | -- @param _carrier nginx request
32 | -- @param _getter getter for interacting with carrier
33 | -- @return nil
34 | --------------------------------------------------------------------------------
35 | function _M:extract(context, _carrier, _getter)
36 | return context
37 | end
38 |
39 | return _M
40 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/span_context.lua:
--------------------------------------------------------------------------------
1 | local tracestate = require("opentelemetry.trace.tracestate")
2 | local _M = {
3 | INVALID_TRACE_ID = "00000000000000000000000000000000",
4 | INVALID_SPAN_ID = "0000000000000000"
5 | }
6 |
7 | local mt = {
8 | __index = _M
9 | }
10 |
11 | function _M.new(tid, sid, trace_flags, trace_state, remote)
12 | local self = {
13 | trace_id = tid,
14 | span_id = sid,
15 | trace_flags = trace_flags,
16 | trace_state = trace_state or tracestate.new({}),
17 | remote = remote,
18 | }
19 | return setmetatable(self, mt)
20 | end
21 |
22 | function _M.is_valid(self)
23 | if self.trace_id == _M.INVALID_TRACE_ID or self.trace_id == nil then
24 | return false
25 | end
26 |
27 | if self.span_id == _M.INVALID_SPAN_ID or self.span_id == nil then
28 | return false
29 | end
30 |
31 | return true
32 | end
33 |
34 | function _M.is_remote(self)
35 | return self.remote
36 | end
37 |
38 | function _M.is_sampled(self)
39 | return bit.band(self.trace_flags, 1) == 1
40 | end
41 |
42 | function _M.plain(self)
43 | return {
44 | trace_id = self.trace_id,
45 | span_id = self.span_id,
46 | trace_flags = self.trace_flags,
47 | trace_state = self.trace_state,
48 | remote = self.remote,
49 | }
50 | end
51 |
52 | return _M
53 |
--------------------------------------------------------------------------------
/t/trace/span_timestamps.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: span start and end timestamps can be set explicitly
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local tracer_provider = require("opentelemetry.trace.tracer_provider").new()
16 | local context = require("opentelemetry.context").new()
17 | local span_context_new = require("opentelemetry.trace.span_context").new
18 | local span_kind = require("opentelemetry.trace.span_kind")
19 | local attr = require("opentelemetry.attribute")
20 | local tracer = tracer_provider:tracer("unit_test")
21 | local context, recording_span = tracer:start(context, "recording",
22 | {kind = span_kind.producer, attributes = {attr.string("key", "value")}}, 123456788)
23 | context.sp:finish(123456789)
24 | if context.sp.start_time ~= 123456788 then
25 | ngx.log(ngx.ERR, "start time should have been 123456788, was " .. context.sp.start_time)
26 | end
27 | if context.sp.end_time ~= 123456789 then
28 | ngx.log(ngx.ERR, "end time should have been 123456789, was " .. context.sp.end_time)
29 | end
30 | }
31 | }
32 | --- request
33 | GET /t
34 | --- no_error_log
35 | 123456789
36 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/cloudevents.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.cloudevents
5 | local _M = {
6 | -- The [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id) uniquely identifies the event.
7 | CLOUDEVENTS_EVENT_ID = "cloudevents.event_id",
8 | -- The [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1) identifies the context in which an event happened.
9 | CLOUDEVENTS_EVENT_SOURCE = "cloudevents.event_source",
10 | -- The [version of the CloudEvents specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion) which the event uses.
11 | CLOUDEVENTS_EVENT_SPEC_VERSION = "cloudevents.event_spec_version",
12 | -- The [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type) contains a value describing the type of event related to the originating occurrence.
13 | CLOUDEVENTS_EVENT_TYPE = "cloudevents.event_type",
14 | -- The [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) of the event in the context of the event producer (identified by source).
15 | CLOUDEVENTS_EVENT_SUBJECT = "cloudevents.event_subject"
16 | }
17 | return _M
18 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/exporter/console.lua:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | -- The console span exporter is used for debugging. It should not be used in
3 | -- production contexts.
4 | -------------------------------------------------------------------------------
5 | local encoder = require("opentelemetry.trace.exporter.encoder")
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M
12 | }
13 |
14 | --------------------------------------------------------------------------------
15 | -- Create a new console span exporter. If being run in an nginx context, will
16 | -- log spans to ngx.log(ngx.INFO). Otherwise, will use Lua's print() method.
17 | --
18 | -- @return New console span exporter
19 | --------------------------------------------------------------------------------
20 | function _M.new()
21 | return setmetatable({}, mt)
22 | end
23 |
24 | function _M.export_spans(self, spans)
25 | local span_string = ""
26 | for _, span in ipairs(spans) do
27 | span_string = span_string .. encoder.for_console(span) .. "\n"
28 | end
29 |
30 | -- Check if ngx variable is not nil; use ngx.log if ngx var is present.
31 | if ngx then
32 | ngx.log(ngx.INFO, "Export spans: ", span_string)
33 | else
34 | print("Export spans: ", span_string)
35 | end
36 | end
37 |
38 | function _M.force_flush(self)
39 | end
40 |
41 | function _M.shutdown(self)
42 | end
43 |
44 | return _M
45 |
--------------------------------------------------------------------------------
/spec/baggage_spec.lua:
--------------------------------------------------------------------------------
1 | local baggage = require("opentelemetry.baggage")
2 |
3 | describe("set_value and get_value", function()
4 | it("sets a value and returns baggage instance", function()
5 | local baggage = baggage.new({ oldkey = { value = "wat" } })
6 | local new_baggage = baggage:set_value("keyname", "val", "metadatastring")
7 | assert.are.equal(new_baggage:get_value("keyname"), "val")
8 | assert.are.equal(new_baggage:get_value("oldkey"), "wat")
9 | end)
10 |
11 | it("overwrites keys", function()
12 | local baggage = baggage.new({ oldkey = { value = "wat" } })
13 | local new_baggage = baggage:set_value("oldkey", "newvalue")
14 | assert.are.equal(new_baggage:get_value("oldkey"), "newvalue")
15 | end)
16 | end)
17 |
18 | describe("get__all_values", function()
19 | it("returns all values", function()
20 | local values = { key1 = { value = "wat", metadata = "ignore" }, key2 = { value = "wat2", metadata } }
21 | local baggage = baggage.new(values)
22 | assert.are.same(baggage:get_all_values(), values)
23 | end)
24 | end)
25 |
26 | describe("remove_value", function()
27 | it("returns new baggage instance without value", function()
28 | local values = { key1 = { value = "wat" }, key2 = { value = "wat2" } }
29 | local baggage = baggage.new(values)
30 | local new_baggage = baggage:remove_value("key1")
31 | assert.are.same(new_baggage:get_all_values(), { key2 = { value = "wat2" } })
32 | end)
33 | end)
34 |
--------------------------------------------------------------------------------
/doc/modules/opentelemetry.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | Reference
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
ldoc
28 |
29 |
32 |
33 |
34 |
35 |
Modules
36 |
40 |
41 |
42 |
43 |
44 |
45 |
Module opentelemetry
46 |
The entrypoint for the package.
47 |
48 | !!! Requiring this file sets a global of __OTEL!
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
generated by LDoc 1.4.6
62 |
Last updated 2023-01-18 22:27:32
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/sampling/parent_base_sampler.lua:
--------------------------------------------------------------------------------
1 | local result_new = require("opentelemetry.trace.sampling.result").new
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | ------------------------------------------------------------------
11 | -- a composite sampler which behaves differently,
12 | -- based on the parent of the span. If the span has no parent,
13 | -- the root(Sampler) is used to make sampling decision. If the span has
14 | -- a parent, depending on whether the parent is sampled.
15 | --
16 | -- @root sampler
17 | -- @return sampler
18 | ------------------------------------------------------------------
19 | function _M.new(root)
20 | return setmetatable({root = root}, mt)
21 | end
22 |
23 | function _M.should_sample(self, parameters)
24 | local parent_span_ctx = parameters.parent_ctx:span_context()
25 | local trace_state = parent_span_ctx.trace_state
26 | if parent_span_ctx:is_valid() then
27 | if parent_span_ctx:is_remote() then
28 | if parent_span_ctx:is_sampled() then
29 | return result_new(2, trace_state)
30 | end
31 | return result_new(0, trace_state)
32 | end
33 |
34 | if parent_span_ctx:is_sampled() then
35 | return result_new(2, trace_state)
36 | end
37 | return result_new(0, trace_state)
38 | end
39 |
40 | return self.root:should_sample(parameters)
41 | end
42 |
43 | function _M.description(self)
44 | return string.format("ParentBased{root:%s}", self.root:description())
45 | end
46 |
47 | return _M
48 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Running Tests
4 |
5 | There are a few different types of tests in this repo, which can all be run via the Makefile.
6 |
7 | ### Integration tests
8 |
9 | There are integration tests in the `t/` directory. These use Perl's `Test::NGINX` framework, as described in the [Programming Openresty Book](https://openresty.gitbooks.io/programming-openresty/content/testing/test-nginx.html). To run these tests, you first must start a working openresty server, which is configured to use the code in the repo (`make openresty-dev`). Then, you can run tests using `make openresty-unit-test`.
10 |
11 | ```
12 | make openresty-build
13 | make openresty-dev
14 | make openresty-unit-test
15 | make openresty-test-e2e
16 | ```
17 |
18 | To pick up code changes, you need to re-run `luarocks make && nginx -s reload` inside the `openresty` container started by `make openresty-dev`.
19 |
20 | ### Tracecontext tests
21 |
22 | There's a test suite in the [w3c/trace-context repo](https://github.com/w3c/trace-context/) that we run against our code and nginx.conf. To run these:
23 |
24 | ```
25 | make openresty-dev
26 | make openresty-test-e2e-trace-context
27 | ```
28 |
29 | ### Unit tests
30 |
31 | There's two sets of unit tests oriented around different runtimes.
32 |
33 | ```
34 | # Run tests oriented around Openresty
35 | make lua-unit-test
36 |
37 | # Run tests that should pass in any Lua runtime
38 | make api-test
39 | ```
40 |
41 |
42 | ## Community
43 |
44 | This project is not officially part of the OpenTelemetry org yet, but you can find some folks in this [Slack channel](https://cloud-native.slack.com/archives/C048T6NFQTY) in [CNCF Slack](https://communityinviter.com/apps/cloud-native/cncf).
45 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua:
--------------------------------------------------------------------------------
1 | local result_new = require("opentelemetry.trace.sampling.result").new
2 | local always_on_sampler_new = require("opentelemetry.trace.sampling.always_on_sampler").new
3 |
4 | local _M = {
5 | }
6 |
7 | local mt = {
8 | __index = _M
9 | }
10 |
11 | ------------------------------------------------------------------
12 | -- samples a given fraction of traces. Fractions >= 1 will
13 | -- always sample. Fractions < 0 are treated as zero. To respect the
14 | -- parent trace's sampled_flag, the trace_id_ratio_based sampler should be used
15 | -- as a delegate of a parent base sampler.
16 | --
17 | -- @return sampler
18 | ------------------------------------------------------------------
19 | function _M.new(fraction)
20 | if fraction >= 1 then
21 | return always_on_sampler_new()
22 | end
23 |
24 | if fraction < 0 then
25 | fraction = 0
26 | end
27 |
28 | return setmetatable({
29 | trace_id_upper_bound = fraction * 0xffffffff,
30 | description = string.format("TraceIDRatioBased{%d}", fraction)
31 | }, mt)
32 | end
33 |
34 | function _M.should_sample(self, parameters)
35 | local parent_span_ctx = parameters.parent_ctx:span_context()
36 | local n = 0
37 | local trace_id = parameters.trace_id
38 | for i = 1, 8, 2 do
39 | n = tonumber(string.sub(trace_id, i, i + 1), 16) + (n * (2 ^ 8))
40 | end
41 |
42 | if n < self.trace_id_upper_bound then
43 | return result_new(2, parent_span_ctx.trace_state)
44 | end
45 |
46 | return result_new(0, parent_span_ctx.trace_state)
47 | end
48 |
49 | function _M.description(self)
50 | return self.description
51 | end
52 |
53 | return _M
54 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/exporter/http_client.lua:
--------------------------------------------------------------------------------
1 | local http = require("resty.http")
2 | local net_url = require("net.url")
3 |
4 | local _M = {
5 | }
6 |
7 | local mt = {
8 | __index = _M
9 | }
10 |
11 | local function build_uri(address)
12 | local parsed_address = net_url.parse(address)
13 | if parsed_address.scheme ~= "http" and parsed_address.scheme ~= "https" then
14 | return build_uri("http://" .. address)
15 | end
16 | if parsed_address.path == "" or parsed_address.path == "/" then
17 | parsed_address.path = "/v1/traces"
18 | end
19 | return tostring(parsed_address)
20 | end
21 |
22 | ------------------------------------------------------------------
23 | -- create a http client used by exporter.
24 | --
25 | -- @address opentelemetry collector: host:port
26 | -- @timeout export request timeout second
27 | -- @headers export request headers
28 | -- @return http client
29 | ------------------------------------------------------------------
30 | function _M.new(address, timeout, headers)
31 | headers = headers or {}
32 | headers["Content-Type"] = "application/x-protobuf"
33 |
34 | local self = {
35 | uri = build_uri(address),
36 | timeout = timeout,
37 | headers = headers,
38 | }
39 | return setmetatable(self, mt)
40 | end
41 |
42 | function _M.do_request(self, body)
43 | local httpc = http.new()
44 | httpc:set_timeout(self.timeout * 1000)
45 |
46 | local res, err = httpc:request_uri(self.uri, {
47 | method = "POST",
48 | headers = self.headers,
49 | body = body,
50 | })
51 |
52 | if not res then
53 | ngx.log(ngx.ERR, "request failed: ", err)
54 | httpc:close()
55 | return nil, err
56 | end
57 |
58 | if res.status ~= 200 then
59 | ngx.log(ngx.ERR, "request failed: ", res.body)
60 | httpc:close()
61 | return nil, "request failed: " .. res.status
62 | end
63 |
64 | return res, nil
65 | end
66 |
67 | return _M
68 |
--------------------------------------------------------------------------------
/lib/opentelemetry/metrics_reporter.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- This defines an interface used for reporting metrics. It defaults to a noop.
3 | -- Users can supply a module that satisfies this interface to actually generate
4 | -- metrics.
5 | --------------------------------------------------------------------------------
6 |
7 | local _M = {}
8 |
9 | --------------------------------------------------------------------------------
10 | -- Adds an increment to a metric with the provided labels. This should be used
11 | -- with counter metrics
12 | --
13 | -- @param metric The metric to increment
14 | -- @param increment The amount to increment the metric by
15 | -- @param labels The labels to use for the metric
16 | -- return nil
17 | --------------------------------------------------------------------------------
18 | function _M:add_to_counter(metric, increment, labels)
19 | return nil
20 | end
21 |
22 | --------------------------------------------------------------------------------
23 | -- Record a value for metric with provided labels. This should be used with
24 | -- histogram or distribution metrics.
25 | --
26 | -- @param metric The metric to record a value for
27 | -- @param value The value to set for the metric
28 | -- @param labels The labels to use for the metric
29 | -- return nil
30 | --------------------------------------------------------------------------------
31 | function _M:record_value(metric, value, labels)
32 | return nil
33 | end
34 |
35 | --------------------------------------------------------------------------------
36 | -- Observe a value for a metric with provided labels. This corresponds to the
37 | -- gauge metric type in datadog
38 | --
39 | -- @param metric The metric to record a value for
40 | -- @param value The value to set for the metric
41 | -- @param labels The labels to use for the metric
42 | -- return nil
43 | --------------------------------------------------------------------------------
44 | function _M:observe_value(metric, value, labels)
45 | return nil
46 | end
47 |
48 | return _M
49 |
--------------------------------------------------------------------------------
/spec/trace/exporter/circuit_spec.lua:
--------------------------------------------------------------------------------
1 | local util = require("lib.opentelemetry.util")
2 | local circuit = require("opentelemetry.trace.exporter.circuit")
3 |
4 | describe("should_make_request", function()
5 | it("returns true when circuit is closed", function()
6 | local c = circuit.new()
7 | c.state = c.CLOSED
8 | assert.is_true(c:should_make_request())
9 | end)
10 |
11 | it("returns false when circuit is open and halfopen_threshold not exceeded", function()
12 | local c = circuit.new({ reset_timeout_ms = 5000 })
13 | c.state = c.OPEN
14 | c.open_start_time_ms = util.gettimeofday_ms()
15 | assert.is_false(c:should_make_request())
16 | end)
17 |
18 | it("returns true when circuit is open and halfopen_threshold is exceeded", function()
19 | local c = circuit.new({ reset_timeout_ms = 5000 })
20 | c.state = c.OPEN
21 | c.open_start_time_ms = util.gettimeofday_ms() - 6000
22 | assert.is_true(c:should_make_request())
23 | end)
24 | end)
25 |
26 | describe("record_failure", function()
27 | it("opens circuit if failure count > self.failure_threshold", function()
28 | local c = circuit.new({ failure_threshold = 1 })
29 | assert.is_equal(c.state, c.CLOSED)
30 | assert.is_equal(c.open_start_time_ms, nil)
31 | c:record_failure()
32 | assert.is_equal(c.state, c.OPEN)
33 | assert.are_not.equals(c.open_start_time_ms, nil)
34 | end)
35 |
36 | it("opens circuit if half-open on entry", function()
37 | local c = circuit.new({ failure_threshold = 5 })
38 | c.state = c.HALF_OPEN
39 | c:record_failure()
40 | assert.is_equal(c.state, c.OPEN)
41 | assert.are_not.equals(c.open_start_time_ms, nil)
42 | end)
43 | end)
44 |
45 | describe("record_success", function()
46 | it("closes circuit if circuit was half-open", function()
47 | local c = circuit.new({ failure_threshold = 1 })
48 | c.state = c.HALF_OPEN
49 | c:record_success()
50 | assert.is_equal(c.state, c.CLOSED)
51 | end)
52 | end)
53 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/rpc.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.rpc
5 | local _M = {
6 | -- A string identifying the remoting system. See below for a list of well-known identifiers.
7 | RPC_SYSTEM = "rpc.system",
8 | -- The full (logical) name of the service being called, including its package name, if applicable.
9 | RPC_SERVICE = "rpc.service",
10 | -- The name of the (logical) method being called, must be equal to the $method part in the span name.
11 | RPC_METHOD = "rpc.method",
12 | -- The [numeric status code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of the gRPC request.
13 | RPC_GRPC_STATUS_CODE = "rpc.grpc.status_code",
14 | -- Protocol version as in `jsonrpc` property of request/response. Since JSON-RPC 1.0 does not specify this, the value can be omitted.
15 | RPC_JSONRPC_VERSION = "rpc.jsonrpc.version",
16 | -- `id` property of request or response. Since protocol allows id to be int, string, `null` or missing (for notifications), value is expected to be cast to string for simplicity. Use empty string in case of `null` value. Omit entirely if this is a notification.
17 | RPC_JSONRPC_REQUEST_ID = "rpc.jsonrpc.request_id",
18 | -- `error.code` property of response if it is an error response.
19 | RPC_JSONRPC_ERROR_CODE = "rpc.jsonrpc.error_code",
20 | -- `error.message` property of response if it is an error response.
21 | RPC_JSONRPC_ERROR_MESSAGE = "rpc.jsonrpc.error_message",
22 | -- Whether this is a received or sent message.
23 | MESSAGE_TYPE = "message.type",
24 | -- MUST be calculated as two different counters starting from `1` one for sent messages and one for received message.
25 | MESSAGE_ID = "message.id",
26 | -- Compressed size of the message in bytes.
27 | MESSAGE_COMPRESSED_SIZE = "message.compressed_size",
28 | -- Uncompressed size of the message in bytes.
29 | MESSAGE_UNCOMPRESSED_SIZE = "message.uncompressed_size"
30 | }
31 | return _M
32 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- The composite propagator bundles together multiple propagators and executes
3 | -- them in sequence.
4 | -- See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md#composite-propagator
5 | --------------------------------------------------------------------------------
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M
12 | }
13 |
14 | --------------------------------------------------------------------------------
15 | -- Returns a new composite propagator. Propagators must adhere to the API
16 | -- defined in the spec
17 | -- See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md
18 | --
19 | -- @param propagators
20 | -- @return a new composite propagator
21 | --------------------------------------------------------------------------------
22 | function _M.new(propagators)
23 | return setmetatable({ propagators = propagators }, mt)
24 | end
25 |
26 | --------------------------------------------------------------------------------
27 | -- Uses the propagators to inject context into carrier in sequence.
28 | --
29 | -- @param context context module
30 | -- @param carrier carrier (e.g. ngx.req)
31 | --------------------------------------------------------------------------------
32 | function _M:inject(context, carrier)
33 | for i = 1, #self.propagators do
34 | self.propagators[i]:inject(context, carrier)
35 | end
36 | end
37 |
38 | --------------------------------------------------------------------------------
39 | -- Uses the propagators to extract context into carrier in sequence.
40 | --
41 | -- @param context context module
42 | -- @param carrier carrier (e.g. ngx.req)
43 | --------------------------------------------------------------------------------
44 | function _M:extract(context, carrier)
45 | local new_ctx = context
46 | for i = 1, #self.propagators do
47 | new_ctx = self.propagators[i]:extract(new_ctx, carrier)
48 | end
49 | return new_ctx
50 | end
51 |
52 | return _M
53 |
--------------------------------------------------------------------------------
/lib/opentelemetry/attribute.lua:
--------------------------------------------------------------------------------
1 | local _M = {}
2 |
3 | function _M.string(key, value)
4 | return {
5 | key = key,
6 | value = {
7 | string_value = value,
8 | }
9 | }
10 | end
11 |
12 | function _M.string_array(key, values)
13 | local ret = {
14 | key = key,
15 | value = {
16 | array_value = {
17 | values = {}
18 | }
19 | }
20 | }
21 | for i = 1, #values do
22 | table.insert(ret.value.array_value.values, {string_value = values[i]})
23 | end
24 | return ret
25 | end
26 |
27 | function _M.int(key, value)
28 | return {
29 | key = key,
30 | value = {
31 | int_value = value,
32 | }
33 | }
34 | end
35 |
36 | function _M.int_array(key, values)
37 | local ret = {
38 | key = key,
39 | value = {
40 | array_value = {
41 | values = {}
42 | }
43 | }
44 | }
45 | for i = 1, #values do
46 | table.insert(ret.value.array_value.values, {int_value = values[i]})
47 | end
48 | return ret
49 | end
50 |
51 | function _M.bool(key, value)
52 | return {
53 | key = key,
54 | value = {
55 | bool_value = value,
56 | }
57 | }
58 | end
59 |
60 | function _M.bool_array(key, values)
61 | local ret = {
62 | key = key,
63 | value = {
64 | array_value = {
65 | values = {}
66 | }
67 | }
68 | }
69 | for i = 1, #values do
70 | table.insert(ret.value.array_value.values, {bool_value = values[i]})
71 | end
72 | return ret
73 | end
74 |
75 | function _M.double(key, value)
76 | return {
77 | key = key,
78 | value = {
79 | double_value = value,
80 | }
81 | }
82 | end
83 |
84 | function _M.double_array(key, values)
85 | local ret = {
86 | key = key,
87 | value = {
88 | array_value = {
89 | values = {}
90 | }
91 | }
92 | }
93 | for i = 1, #values do
94 | table.insert(ret.value.array_value.values, {double_value = values[i]})
95 | end
96 | return ret
97 | end
98 |
99 | return _M
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/faas.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.faas
5 | local _M = {
6 | -- Type of the trigger which caused this function execution.
7 | FAAS_TRIGGER = "faas.trigger",
8 | -- The execution ID of the current function execution.
9 | FAAS_EXECUTION = "faas.execution",
10 | -- The name of the source on which the triggering operation was performed. For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name.
11 | FAAS_DOCUMENT_COLLECTION = "faas.document.collection",
12 | -- Describes the type of the operation that was performed on the data.
13 | FAAS_DOCUMENT_OPERATION = "faas.document.operation",
14 | -- A string containing the time when the data was accessed in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime).
15 | FAAS_DOCUMENT_TIME = "faas.document.time",
16 | -- The document name/table subjected to the operation. For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name.
17 | FAAS_DOCUMENT_NAME = "faas.document.name",
18 | -- A string containing the function invocation time in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format expressed in [UTC](https://www.w3.org/TR/NOTE-datetime).
19 | FAAS_TIME = "faas.time",
20 | -- A string containing the schedule period as [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm).
21 | FAAS_CRON = "faas.cron",
22 | -- A boolean that is true if the serverless function is executed for the first time (aka cold-start).
23 | FAAS_COLDSTART = "faas.coldstart",
24 | -- The name of the invoked function.
25 | FAAS_INVOKED_NAME = "faas.invoked_name",
26 | -- The cloud provider of the invoked function.
27 | FAAS_INVOKED_PROVIDER = "faas.invoked_provider",
28 | -- The cloud region of the invoked function.
29 | FAAS_INVOKED_REGION = "faas.invoked_region"
30 | }
31 | return _M
32 |
--------------------------------------------------------------------------------
/t/trace/sampling/trace_id_ratio_sampler.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: trace_id_ratio_sampler:should_sample
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local trace_id_ratio_sampler_new = require("opentelemetry.trace.sampling.trace_id_ratio_sampler").new
16 | local context_new = require("opentelemetry.context").new
17 |
18 | ngx.say("test fraction = 0")
19 | local result = trace_id_ratio_sampler_new(0):should_sample({
20 | parent_ctx = context_new(),
21 | trace_id = "00000000000000000000000000000000",
22 | })
23 | if result:is_sampled() then
24 | ngx.say("expect result:is_sampled() == false")
25 | return
26 | end
27 |
28 | ngx.say("test fraction = 1")
29 | local result = trace_id_ratio_sampler_new(1):should_sample({
30 | parent_ctx = context_new(),
31 | trace_id = "ffffffff000000000000000000000000",
32 | })
33 | if not result:is_sampled() then
34 | ngx.say("expect result:is_sampled() == true")
35 | return
36 | end
37 |
38 | ngx.say("test fraction = 0.5")
39 | local result = trace_id_ratio_sampler_new(0.5):should_sample({
40 | parent_ctx = context_new(),
41 | trace_id = "7fffffff000000000000000000000000",
42 | })
43 | if not result:is_sampled() then
44 | ngx.say("expect result:is_sampled() == true")
45 | return
46 | end
47 |
48 | ngx.say("test fraction = 0.5")
49 | local result = trace_id_ratio_sampler_new(0.5):should_sample({
50 | parent_ctx = context_new(),
51 | trace_id = "80000000000000000000000000000000",
52 | })
53 | if result:is_sampled() then
54 | ngx.say("expect result:is_sampled() == false")
55 | return
56 | end
57 |
58 | ngx.say("done")
59 | }
60 | }
61 | --- request
62 | GET /t
63 | --- error_code: 200
64 | --- response_body
65 | test fraction = 0
66 | test fraction = 1
67 | test fraction = 0.5
68 | test fraction = 0.5
69 | done
70 | --- no_error_log
71 | [error]
72 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: doc format api-test benchmark
2 | CONTAINER_ORCHESTRATOR ?= docker-compose
3 | CONTAINER_ORCHESTRATOR_EXEC_OPTIONS := $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS)
4 |
5 | openresty-dev:
6 | $(CONTAINER_ORCHESTRATOR) up -d openresty
7 | $(CONTAINER_ORCHESTRATOR) exec $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'luarocks make && nginx -s reload'
8 |
9 | openresty-test-e2e:
10 | $(CONTAINER_ORCHESTRATOR) run -e PROXY_ENDPOINT=http://openresty/test/e2e --rm test-client
11 |
12 | openresty-test-e2e-trace-context:
13 | $(CONTAINER_ORCHESTRATOR) exec $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash /opt/opentelemetry-lua/e2e-trace-context.sh
14 |
15 | openresty-integration-test:
16 | $(CONTAINER_ORCHESTRATOR) exec $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'cd /opt/opentelemetry-lua && prove -r'
17 |
18 | lua-unit-test:
19 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c './busted-runner'
20 |
21 | openresty-build:
22 | $(CONTAINER_ORCHESTRATOR) build
23 |
24 | doc:
25 | $(CONTAINER_ORCHESTRATOR) run $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'ldoc lib/opentelemetry/api'
26 |
27 | check-format:
28 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- utils bash -c 'lua-format --check lib/opentelemetry/api/**/*.lua spec/api/**/*.lua'
29 |
30 | format:
31 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- utils bash -c 'lua-format -i lib/opentelemetry/api/**/*.lua spec/api/**/*.lua'
32 |
33 | api-test:
34 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'busted -m "./lib/?.lua;./lib/?/?.lua;./lib/?/?/?.lua" ./spec/api'
35 |
36 | generate-semantic-conventions:
37 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'pushd tmp && rm -rf opentelemetry-specification && git clone --depth=1 https://github.com/open-telemetry/opentelemetry-specification.git && popd && resty ./utils/generate_semantic_conventions.lua && lua-format -i lib/opentelemetry/semantic_conventions/trace/*.lua'
38 |
39 | benchmark:
40 | $(CONTAINER_ORCHESTRATOR) run --no-deps $(CONTAINER_ORCHESTRATOR_EXEC_OPTIONS) -- openresty bash -c 'cd /opt/opentelemetry-lua && benchmark/run.sh'
41 |
--------------------------------------------------------------------------------
/spec/trace/tracestate_spec.lua:
--------------------------------------------------------------------------------
1 | local tracestate = require("opentelemetry.trace.tracestate")
2 |
3 | describe("is_valid", function()
4 | it("parse, get works", function()
5 | local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman")
6 | assert.is_true(#ts.values == 2)
7 | assert.is_true(ts:get("foo") == "bar")
8 | assert.is_true(ts:get("baz") == "lehrman")
9 | end)
10 | it("set works", function()
11 | local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman")
12 | assert.is_true(#ts.values == 2)
13 | ts:set("foo", "fun")
14 | assert.is_true(#ts.values == 2)
15 | assert.is_true(ts:get("foo") == "fun")
16 | ts:set("family", "values")
17 | assert.is_true(#ts.values == 3)
18 | assert.is_true(ts:get("family") == "values")
19 | -- setting an invalid value leaves the old kv pair
20 | ts:set("foo", "v=l")
21 | assert.is_true(ts:get("foo") == "fun")
22 | end)
23 | it("del works", function()
24 | local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman")
25 | ts:del("foo")
26 | assert.is_true(#ts.values == 1)
27 | assert.is_true(ts:get("foo") == "")
28 | end)
29 | it("as_string works", function()
30 | local ts = tracestate.parse_tracestate("foo=bar,baz=lehrman")
31 | assert.is_true(ts:as_string() == "foo=bar,baz=lehrman")
32 | ts:set("bing", "bong")
33 | assert.is_true(ts:as_string() == "bing=bong,foo=bar,baz=lehrman")
34 | end)
35 | it("max len is respected", function()
36 | -- Supress logs during test, since we expect them.
37 | stub(ngx, "log")
38 | local ts = tracestate.parse_tracestate("")
39 | for i=1,tracestate.MAX_ENTRIES,1 do
40 | ts:set("a" .. tostring(i), "b" .. tostring(i))
41 | end
42 | assert.is_true(#ts.values == tracestate.MAX_ENTRIES)
43 | ts:set("one", "more")
44 | ngx.log:revert()
45 | assert.is_true(#ts.values == tracestate.MAX_ENTRIES)
46 | -- First elem added is the first one lost when we add over max entries
47 | assert.is_true(ts:get("a1") == "")
48 | assert.is_true(ts:get("one") == "more")
49 | -- Newest elem is prepended
50 | assert.is_true(ts.values[1][1] == "one")
51 | end)
52 | end)
53 |
--------------------------------------------------------------------------------
/doc/index.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | Reference
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
ldoc
28 |
29 |
30 |
Contents
31 |
34 |
35 |
36 |
Modules
37 |
38 | - api.trace.span_status
39 |
40 |
41 |
42 |
43 |
44 |
45 |
Module api.trace.span_status
46 |
Span status represents the status of a span.
47 |
Like an HTTP code, but for your span. It is either unset, ok, or error.
48 |
49 |
50 |
51 |
52 |
53 |
54 | | new (code) |
55 | Returns a new span_status. |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | -
67 |
68 | new (code)
69 |
70 | -
71 | Returns a new span_status.
72 |
73 |
74 |
Parameters:
75 |
76 | - code
77 | int
78 | The status code. Defaults to UNSET.
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
generated by LDoc 1.4.6
94 |
Last updated 2023-01-18 23:39:56
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/t/trace/tracer.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: tracer:start
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local tracer_provider = require("opentelemetry.trace.tracer_provider").new()
16 | local context = require("opentelemetry.context").new()
17 | local span_context_new = require("opentelemetry.trace.span_context").new
18 | local span_kind = require("opentelemetry.trace.span_kind")
19 | local attr = require("opentelemetry.attribute")
20 | local tracer = tracer_provider:tracer("unit_test")
21 |
22 | ngx.say("test start recording_span")
23 | local context, recording_span = tracer:start(context, "recording",
24 | {kind = span_kind.producer, attributes = {attr.string("key", "value")}})
25 | if not (recording_span.name == "recording" and recording_span.start_time > 0
26 | and recording_span.kind == span_kind.producer
27 | and #recording_span.attributes == 1
28 | and recording_span.attributes[1].key == "key"
29 | and recording_span.attributes[1].value.string_value == "value") then
30 | ngx.say("unexpected recording_span")
31 | end
32 | if not recording_span:is_recording() then
33 | ngx.say("expect recording")
34 | end
35 |
36 | ngx.say("test start non_recording_span")
37 | local always_off_sampler = require("opentelemetry.trace.sampling.always_off_sampler").new()
38 | tracer_provider.sampler = always_off_sampler
39 | local context, non_recording_span = tracer:start(context, "non_recording",
40 | {kind = span_kind.consumer, attributes = {attr.string("key", "value")}})
41 | if not (non_recording_span.name == nil and non_recording_span.start_time == nil
42 | and non_recording_span.kind == nil
43 | and non_recording_span.attributes == nil) then
44 | ngx.say("unexpected non_recording_span")
45 | end
46 | if non_recording_span:is_recording() then
47 | ngx.say("expect non_recording")
48 | end
49 |
50 | ngx.say("done")
51 | }
52 | }
53 | --- request
54 | GET /t
55 | --- error_code: 200
56 | --- response_body
57 | test start recording_span
58 | test start non_recording_span
59 | done
60 | --- no_error_log
61 | [error]
--------------------------------------------------------------------------------
/t/context.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: context
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local span_context_new = require("opentelemetry.trace.span_context").new
16 | local context = require("opentelemetry.context").new()
17 |
18 | ngx.say("text context:with_span_context")
19 | context = context:with_span_context(span_context_new("trace_id", "span_id", 1, "trace_state", false))
20 | local span_context = context:span_context()
21 | if not (span_context.trace_id == "trace_id" and span_context.span_id == "span_id" and
22 | span_context.trace_flags == 1 and span_context.trace_state == "trace_state" and
23 | span_context.remote == false) then
24 | ngx.say("unexpected span_context")
25 | end
26 | if context:span():is_recording() then
27 | ngx.say("unexpected span")
28 | end
29 |
30 | ngx.say("test context attach & detach")
31 | local tracer_provider = require("opentelemetry.trace.tracer_provider").new()
32 | local tracer = tracer_provider:tracer("unit_test")
33 | local context, recording_span = tracer:start(context, "recording")
34 | if context.current().sp:is_recording() ~= false then
35 | ngx.say("expected context.current() span to be non-recording")
36 | end
37 | context:attach()
38 | if context.current():span().name ~= "recording" then
39 | ngx.say("unexpected context.current():span()")
40 | end
41 | context, recording_span = tracer:start(context, "recording2")
42 | context:attach()
43 | if context.current():span().name ~= "recording2" then
44 | ngx.say("unexpected context.current():span()")
45 | end
46 | context:detach(2)
47 | if context.current():span().name ~= "recording" then
48 | ngx.say("unexpected context.current():span()")
49 | end
50 | context.current():detach(1)
51 | if context.current() ~= nil then
52 | ngx.say("unexpected context.current()")
53 | end
54 | ngx.say("done")
55 | }
56 | }
57 | --- request
58 | GET /t
59 | --- error_code: 200
60 | --- response_body
61 | text context:with_span_context
62 | test context attach & detach
63 | done
64 | --- no_error_log
65 | [error]
66 |
--------------------------------------------------------------------------------
/doc/modules/api.trace.span_status.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | Reference
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
ldoc
28 |
29 |
32 |
33 |
Contents
34 |
37 |
38 |
39 |
Modules
40 |
44 |
45 |
46 |
47 |
48 |
49 |
Module api.trace.span_status
50 |
Span status represents the status of a span.
51 |
Like an HTTP code, but for your span. It is either unset, ok, or error.
52 |
53 |
54 |
55 |
56 |
57 |
58 | | new (code) |
59 | Returns a new span_status. |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | -
71 |
72 | new (code)
73 |
74 | -
75 | Returns a new span_status.
76 |
77 |
78 |
Parameters:
79 |
80 | - code
81 | int
82 | The status code. Defaults to UNSET.
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
generated by LDoc 1.4.6
98 |
Last updated 2023-01-18 22:27:32
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/lib/opentelemetry/baggage.lua:
--------------------------------------------------------------------------------
1 | local util = require("opentelemetry.util")
2 |
3 | local _M = {
4 | }
5 |
6 | local mt = {
7 | __index = _M
8 | }
9 |
10 | function _M.new(values)
11 | return setmetatable({ values = values or {} }, mt)
12 | end
13 |
14 | --------------------------------------------------------------------------------
15 | -- Set a value in a baggage instance. Does _not_ inject into context
16 | --
17 | -- @name name for which to set the value in baggage
18 | -- @value value to set must be string
19 | -- @metadata metadata to set in baggage (string)
20 | -- @return baggage
21 | --------------------------------------------------------------------------------
22 | function _M.set_value(self, name, value, metadata)
23 | local new_values = util.shallow_copy_table(self.values)
24 | new_values[name] = { value = value, metadata = metadata }
25 | return self.new(new_values)
26 | end
27 |
28 | --------------------------------------------------------------------------------
29 | -- Get value stored at a specific name in a baggage instance
30 | --
31 | -- @name name for which to set the value in baggage
32 | -- @return baggage
33 | --------------------------------------------------------------------------------
34 | function _M.get_value(self, name)
35 | if self.values[name] then
36 | return self.values[name].value
37 | else
38 | return nil
39 | end
40 | end
41 |
42 | --------------------------------------------------------------------------------
43 | -- Remove value stored at a specific name in a baggage instance.
44 | --
45 | -- @name name to remove from baggage
46 | -- @return baggage
47 | --------------------------------------------------------------------------------
48 | function _M.remove_value(self, name)
49 | local new_values = util.shallow_copy_table(self.values)
50 | new_values[name] = nil
51 | return self.new(new_values)
52 | end
53 |
54 | --------------------------------------------------------------------------------
55 | -- Get all values in a baggage instance. This is supposed to return an immutable
56 | -- collection, but we just return a copy of the table stored at values.
57 | --
58 | -- @context context from which to access the baggage (defaults to
59 | -- current context)
60 | -- @return table like { keyname = { value = "value",
61 | -- metadata = "metadatastring"} }
62 | --------------------------------------------------------------------------------
63 | function _M.get_all_values(self)
64 | return util.shallow_copy_table(self.values)
65 | end
66 |
67 | return _M
68 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/tracer.lua:
--------------------------------------------------------------------------------
1 | local bit = require("bit")
2 | local span_context_new = require("opentelemetry.trace.span_context").new
3 | local non_recording_span_new = require("opentelemetry.trace.non_recording_span").new
4 | local recording_span_new = require("opentelemetry.trace.recording_span").new
5 | local span_kind = require("opentelemetry.trace.span_kind")
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M
12 | }
13 |
14 | function _M.new(provider, il)
15 | local self = {
16 | provider = provider,
17 | il = il
18 | }
19 | return setmetatable(self, mt)
20 | end
21 |
22 | local function new_span(self, context, name, config, start_time)
23 | local span_context = context:span_context()
24 | if not config then
25 | config = {}
26 | end
27 | local trace_id = span_context.trace_id
28 | local span_id
29 | if trace_id then
30 | span_id = self.provider.id_generator.new_span_id(trace_id)
31 | else
32 | trace_id, span_id = self.provider.id_generator.new_ids()
33 | end
34 |
35 | local sampling_result = self.provider.sampler:should_sample({
36 | parent_ctx = context,
37 | trace_id = trace_id,
38 | name = name,
39 | kind = span_kind.validate(config.kind),
40 | attributes = config.attributes,
41 | })
42 |
43 | local trace_flags = span_context.trace_flags and span_context.trace_flags or 0
44 | if sampling_result:is_sampled() then
45 | trace_flags = bit.bor(trace_flags, 1)
46 | else
47 | trace_flags = bit.band(trace_flags, 0)
48 | end
49 | local new_span_context = span_context_new(trace_id, span_id, trace_flags, sampling_result.trace_state, false)
50 |
51 | local span
52 | if not sampling_result:is_recording() then
53 | span = non_recording_span_new(self, new_span_context)
54 | else
55 | span = recording_span_new(self, span_context, new_span_context, name, config, start_time)
56 | end
57 |
58 | return context:with_span(span), span
59 | end
60 |
61 | ------------------------------------------------------------------
62 | -- create a span.
63 | --
64 | -- @context context with parent span
65 | -- @span_name span name
66 | -- @span_start_config [optional]
67 | -- span_start_config.kind: opentelemetry.trace.span_kind.*
68 | -- span_start_config.attributes: a list of attribute
69 | -- @start_time [optional] start time: nanoseconds
70 | -- @return
71 | -- context: new context with span
72 | -- span
73 | ------------------------------------------------------------------
74 | function _M.start(self, context, span_name, span_start_config, start_time)
75 | return new_span(self, context, span_name, span_start_config, start_time)
76 | end
77 |
78 | return _M
79 |
--------------------------------------------------------------------------------
/t/trace/sampling/parent_base_sampler.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | log_level('debug');
4 | repeat_each(1);
5 | no_long_string();
6 | no_root_location();
7 | run_tests();
8 |
9 | __DATA__
10 |
11 | === TEST 1: parent_base_sampler:should_sample
12 | --- config
13 | location = /t {
14 | content_by_lua_block {
15 | local always_on_sampler = require("opentelemetry.trace.sampling.always_on_sampler").new()
16 | local parent_base_sampler = require("opentelemetry.trace.sampling.parent_base_sampler").new(always_on_sampler)
17 | local span_context_new = require("opentelemetry.trace.span_context").new
18 | local context_new = require("opentelemetry.context").new
19 |
20 | ngx.say("test invalid parameters.parent_ctx")
21 | local result = parent_base_sampler:should_sample({
22 | parent_ctx = context_new()
23 | })
24 | if not result:is_sampled() then
25 | ngx.say("expect result:is_sampled() == true")
26 | return
27 | end
28 |
29 | ngx.say("test parameters.parent_ctx{remote = true}")
30 | local result = parent_base_sampler:should_sample({
31 | parent_ctx = context_new():with_span_context(span_context_new("trace_id", "span_id", 0, "trace_state", true))
32 | })
33 | if result:is_sampled() then
34 | ngx.say("expect result:is_sampled() == false")
35 | return
36 | end
37 |
38 | ngx.say("test parameters.parent_ctx{remote = true, sampled = true}")
39 | local result = parent_base_sampler:should_sample({
40 | parent_ctx = context_new():with_span_context(span_context_new("trace_id", "span_id", 1, "trace_state", true))
41 | })
42 | if not result:is_sampled() then
43 | ngx.say("expect result:is_sampled() == true")
44 | return
45 | end
46 |
47 | ngx.say("test parameters.parent_ctx{remote = false, sampled = true}")
48 | local result = parent_base_sampler:should_sample({
49 | parent_ctx = context_new():with_span_context(span_context_new("trace_id", "span_id", 1, "trace_state", true))
50 | })
51 | if not result:is_sampled() then
52 | ngx.say("expect result:is_sampled() == true")
53 | return
54 | end
55 |
56 | ngx.say("test parameters.parent_ctx{remote = false, sampled = false}")
57 | local result = parent_base_sampler:should_sample({
58 | parent_ctx = context_new():with_span_context(span_context_new("trace_id", "span_id", 0, "trace_state", false))
59 | })
60 | if result:is_sampled() then
61 | ngx.say("expect result:is_sampled() == false")
62 | return
63 | end
64 |
65 | ngx.say("done")
66 | }
67 | }
68 | --- request
69 | GET /t
70 | --- error_code: 200
71 | --- response_body
72 | test invalid parameters.parent_ctx
73 | test parameters.parent_ctx{remote = true}
74 | test parameters.parent_ctx{remote = true, sampled = true}
75 | test parameters.parent_ctx{remote = false, sampled = true}
76 | test parameters.parent_ctx{remote = false, sampled = false}
77 | done
78 | --- no_error_log
79 | [error]
80 |
--------------------------------------------------------------------------------
/utils/generate_semantic_conventions.lua:
--------------------------------------------------------------------------------
1 | ------------------------------------------------------------------------------------------------------------------------
2 | -- This script grabs semantic conventions from the opentelemetry specification repository and generates Lua modules
3 | -- for each file.
4 | ------------------------------------------------------------------------------------------------------------------------
5 |
6 | local pl_dir = require('pl.dir')
7 | local pl_file = require('pl.file')
8 | local files_by_dir = {}
9 | local dirs = pl_dir.getdirectories("tmp/opentelemetry-specification/semantic_conventions/trace/")
10 |
11 | for _i, dir in ipairs(dirs) do
12 | print(dir)
13 | table.insert(files_by_dir, pl_dir.getfiles(dir, "*.yaml"))
14 | end
15 |
16 | table.insert(
17 | files_by_dir, pl_dir.getfiles("tmp/opentelemetry-specification/semantic_conventions/trace/", "*.yaml")
18 | )
19 |
20 | local lyaml = require("lyaml")
21 |
22 | for _i, dir in ipairs(files_by_dir) do
23 | for _i, file in ipairs(dir) do
24 | print(file)
25 | local filename = string.match(file, "tmp/opentelemetry%-specification/semantic_conventions/trace/(%a+)")
26 | local outfile = "lib/opentelemetry/semantic_conventions/trace/" .. filename .. ".lua"
27 | local f = io.open(file, "rb")
28 | local content = f:read("*all")
29 | f:close()
30 | local data = lyaml.load(content)
31 | print("Writing semantic conventions to " .. outfile)
32 | local out_str = "--- This file was automatically generated by utils/generate_semantic_conventions.lua\n"
33 | out_str = out_str .. "-- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions\n"
34 | out_str = out_str .. "--\n"
35 | out_str = out_str .. "-- module @semantic_conventions.trace." .. filename .. "\n\n"
36 | out_str = out_str .. "local _M = {"
37 |
38 | local attr_string = ""
39 | for _i, group in ipairs(data.groups) do
40 | local prefix = group.prefix or ""
41 | local prop_prefix = string.upper(string.gsub(prefix, "%.", "_"))
42 | if group.attributes then
43 | for _i, attr in ipairs(group.attributes) do
44 | if attr.id then
45 | local brief = string.gsub((attr.brief or attr.id), "\n", "")
46 | local attr_id_prop = string.upper(string.gsub(attr.id, "%.", "_"))
47 | local attr_id = attr.id
48 | if prefix then
49 | attr_id_prop = prop_prefix .. "_" .. attr_id_prop
50 | attr_id = prefix .. "." .. attr_id
51 | end
52 | local new_attr = "\n-- " .. brief .. "\n" .. attr_id_prop .. " = " .. "\"" .. attr_id .. "\","
53 | attr_string = attr_string .. new_attr
54 | end
55 | end
56 | end
57 | end
58 | pl_file.write(outfile, out_str .. attr_string .. "\n}\n return _M")
59 | end
60 | end
61 |
62 | print("Finished writing semantic conventions. Run `git diff` to see any changes.")
63 |
--------------------------------------------------------------------------------
/spec/trace/propagation/text_map/trace_context_propagator_spec.lua:
--------------------------------------------------------------------------------
1 | local text_map_propagator = require "opentelemetry.trace.propagation.text_map.trace_context_propagator"
2 | local tracer_provider = require "opentelemetry.trace.tracer_provider"
3 | local context = require("opentelemetry.context")
4 |
5 | -- We're setting these on ngx.req but we aren't running openresty in these
6 | -- tests, so we'll mock that out (ngx.req supports get_headers() and set_header(header_name))
7 | local function newCarrier(header, header_return)
8 | local r = { headers = {} }
9 | r.headers[header] = header_return
10 | r.get_headers = function() return r.headers end
11 | r.set_header = function(name, val) r.headers[name] = val end
12 | return r
13 | end
14 |
15 | describe("text map propagator", function()
16 | describe(".fields", function()
17 | local tmp = text_map_propagator.new()
18 | it("should return traceparent and traceheader", function()
19 | assert.are.same({ "traceparent", "tracestate" }, tmp.fields())
20 | end)
21 | end)
22 |
23 | describe(":inject", function()
24 | it("adds traceparent headers to carrier", function()
25 | local tmp = text_map_propagator.new()
26 | local context = context.new()
27 | local carrier = newCarrier("ok", "alright")
28 | local tracer_provider = tracer_provider.new()
29 | local tracer = tracer_provider:tracer("test tracer")
30 | -- start a trace
31 | local new_ctx = tracer:start(context, "span", { kind = 1 })
32 |
33 | -- figure out what traceheader should look like
34 | local span_context = new_ctx.sp:context()
35 | local traceparent = string.format("00-%s-%s-%02x",
36 | span_context.trace_id, span_context.span_id, span_context.trace_flags)
37 |
38 | -- make sure we set_header in the setter
39 | spy.on(carrier, "set_header")
40 | tmp:inject(new_ctx, carrier)
41 | assert.spy(carrier.set_header).was.called_with("traceparent", traceparent)
42 | end)
43 | end)
44 |
45 | describe(":extract", function()
46 | it("does not override existing context when none is present in traceparent", function()
47 | local tmp = text_map_propagator.new()
48 | local old_ctx = context.new()
49 | local trace_id = "10f5b3bcfe3f0c2c5e1ef150fe0b5872"
50 | local carrier = newCarrier("nottraceparent", "doesn't matter")
51 |
52 | local ctx = tmp:extract(old_ctx, carrier)
53 | assert.are.same(ctx, old_ctx)
54 | end)
55 |
56 | it("sets context when traceparent is valid", function()
57 | local tmp = text_map_propagator.new()
58 | local context = context.new()
59 | local trace_id = "10f5b3bcfe3f0c2c5e1ef150fe0b5872"
60 | local carrier = newCarrier("traceparent", string.format("00-%s-172accbce5f048db-01", trace_id))
61 |
62 | local ctx = tmp:extract(context, carrier)
63 | assert.are.same(ctx.sp:context().trace_id, trace_id)
64 | end)
65 | end)
66 | end)
67 |
--------------------------------------------------------------------------------
/examples/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "net/http"
7 | "time"
8 |
9 | "go.opentelemetry.io/otel"
10 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
11 | "go.opentelemetry.io/otel/propagation"
12 | "go.opentelemetry.io/otel/sdk/resource"
13 | sdktrace "go.opentelemetry.io/otel/sdk/trace"
14 | semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
15 | "go.opentelemetry.io/otel/trace"
16 | )
17 |
18 | // Initializes an OTLP exporter, and configures the corresponding trace and
19 | // metric providers.
20 | func initProvider() func() {
21 | ctx := context.Background()
22 |
23 | res, err := resource.New(ctx,
24 | resource.WithAttributes(
25 | // the service name used to display traces in backends
26 | semconv.ServiceNameKey.String("test-server"),
27 | ),
28 | )
29 | handleErr(err, "failed to create resource")
30 |
31 | // If the OpenTelemetry Collector is running on a local cluster (minikube or
32 | // microk8s), it should be accessible through the NodePort service at the
33 | // `localhost:30080` endpoint. Otherwise, replace `localhost` with the
34 | // endpoint of your cluster. If you run the app inside k8s, then you can
35 | // probably connect directly to the service through dns
36 |
37 | // Set up a trace exporter
38 | traceExporter, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("otel-collector:4317"), otlptracehttp.WithInsecure(),
39 | otlptracehttp.WithHeaders(map[string]string{
40 | "Content-Type": "application/json",
41 | }))
42 | handleErr(err, "failed to create trace exporter")
43 |
44 | // Register the trace exporter with a TracerProvider, using a batch
45 | // span processor to aggregate spans before export.
46 | bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
47 | tracerProvider := sdktrace.NewTracerProvider(
48 | sdktrace.WithSampler(sdktrace.AlwaysSample()),
49 | sdktrace.WithResource(res),
50 | sdktrace.WithSpanProcessor(bsp),
51 | )
52 | otel.SetTracerProvider(tracerProvider)
53 |
54 | // set global propagator to tracecontext (the default is no-op).
55 | otel.SetTextMapPropagator(propagation.TraceContext{})
56 |
57 | return func() {
58 | // Shutdown will flush any remaining spans and shut down the exporter.
59 | handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider")
60 | }
61 | }
62 |
63 | func main() {
64 | log.Printf("Waiting for connection...")
65 |
66 | shutdown := initProvider()
67 | defer shutdown()
68 |
69 | tracer := otel.Tracer("test-client-tracer")
70 |
71 | http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
72 | log.Println("request url path: ", request.URL.Path)
73 | log.Println("request headers: ", request.Header)
74 |
75 | ctx := propagation.TraceContext{}.Extract(request.Context(), propagation.HeaderCarrier(request.Header))
76 | ctx, span := tracer.Start(ctx, "test-server-span", trace.WithSpanKind(trace.SpanKindServer))
77 | defer span.End()
78 |
79 | time.Sleep(2 * time.Second)
80 |
81 | writer.WriteHeader(200)
82 | _, _ = writer.Write([]byte("hello lua"))
83 | })
84 |
85 | _ = http.ListenAndServe("0.0.0.0:80", nil)
86 | }
87 |
88 | func handleErr(err error, message string) {
89 | if err != nil {
90 | log.Fatalf("%s: %v", message, err)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Changelog
3 | ---
4 |
5 | ## Table of Contents
6 |
7 | - [0.2-6](#0.2-6)
8 | - [0.2-5](#0.2-5)
9 | - [0.2-4](#0.2-4)
10 | - [0.2-3](#0.2-3)
11 | - [0.2-2](#0.2-2)
12 | - [0.2-1](#0.2-1)
13 | - [0.2-0](#0.2-0)
14 | - [0.1-3](#0.1-3)
15 | - [0.1-2](#0.1-2)
16 | - [0.1-1](#0.1-1)
17 | - [0.1-0](#0.1-0)
18 |
19 |
20 | ## 0.2-6
21 |
22 | ### Change
23 |
24 | - feature: allow user to specify start time for new recording span yangxikun/opentelemetry-lua#86
25 |
26 | ### Bugfix
27 |
28 | - duplicate trace ids in high requests distributed system yangxikun/opentelemetry-lua#95
29 |
30 |
31 | ## 0.2-5
32 |
33 | ### Change
34 |
35 | - feature: attribute support array_value type yangxikun/opentelemetry-lua#82
36 | - improve: add resource attrs to console export yangxikun/opentelemetry-lua#91
37 | - improve: allow multiple tracer providers or tracers for export yangxikun/opentelemetry-lua#92
38 |
39 | ### Bugfix
40 |
41 | - Baggage header parsing should remove leading and trailing whitespaces in k/v, yangxikun/opentelemetry-lua#77
42 |
43 | ## 0.2-4
44 |
45 | ### Change
46 |
47 | - improve: speed up id generator yangxikun/opentelemetry-lua#74
48 | - feature: add semantic conventions yangxikun/opentelemetry-lua#66
49 |
50 | ## 0.2-3
51 |
52 | ### Change
53 |
54 | - feature: exporter client support https yangxikun/opentelemetry-lua#60
55 | - breaking: span_status field name changed to uppercase yangxikun/opentelemetry-lua#59
56 |
57 | ### Bugfix
58 |
59 | - context.with_span need copy the parent context's entries yangxikun/opentelemetry-lua#58
60 |
61 | ## 0.2-2
62 |
63 | ### Change
64 |
65 | - feature: add simple span processor
66 | - enhancement: allow users to specify multiple span processors
67 | - enhancement: allow user to specify end timestamp when finishing span
68 | - feature: add tracestate handling
69 |
70 | ## 0.2-1
71 |
72 | ### Change
73 |
74 | - enhancement: trace exporter add exponential backoff and circuit breaker when failed to exporting spans
75 | - feature: add console exporter for debugging
76 | - feature: support [baggage](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/baggage/api.md)
77 | - feature: add metrics reporter
78 | - feature: add tracestate to exports
79 | - breaking: refactor context, `context.attach` will return a token, and need to be passed to `context.detach`
80 |
81 | ### Bugfix
82 |
83 | - fix data race in batch_span_processor
84 |
85 | ## 0.2-0
86 |
87 | ### Change
88 |
89 | - enhancement: support export spans on exit
90 | - enhancement: exporter http_client use keepalive
91 | - breaking: propagation api change
92 |
93 | ## 0.1-3
94 |
95 | ### Bugfix
96 |
97 | - exporter: hex2bytes may panic if traceID is invalid
98 |
99 | ## 0.1-2
100 |
101 | ### Bugfix
102 |
103 | - batch_span_processor export zero length spans. apache/apisix#6329
104 |
105 | ## 0.1-1
106 |
107 | ### Bugfix
108 |
109 | - batch_span_processor fail on openresty log_by_lua phase
110 | - batch_span_processor.shutdown should not set exporter=nil
111 |
112 | ### Change
113 |
114 | - enhancement: batch_span_processor avoid useless timer
115 | - feat: exporter http client support custom headers
116 | - feat: refactor id_generator
117 |
118 | ## 0.1-0
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.1-0.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.1-0"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | tag = "v0.1.0"
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "api7-lua-resty-http = 0.2.0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.context_storage"] = "lib/opentelemetry/context_storage.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
32 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
33 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
34 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
35 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
36 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
37 | ["opentelemetry.trace.propagation.carrier"] = "lib/opentelemetry/trace/propagation/carrier.lua",
38 | ["opentelemetry.trace.propagation.trace_context"] = "lib/opentelemetry/trace/propagation/trace_context.lua",
39 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
40 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
41 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
42 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
43 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
44 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
45 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
46 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
47 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
48 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
49 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
50 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
51 | }
52 | }
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.1-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.1-1"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | tag = "v0.1.1"
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "api7-lua-resty-http = 0.2.0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.context_storage"] = "lib/opentelemetry/context_storage.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
32 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
33 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
34 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
35 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
36 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
37 | ["opentelemetry.trace.propagation.carrier"] = "lib/opentelemetry/trace/propagation/carrier.lua",
38 | ["opentelemetry.trace.propagation.trace_context"] = "lib/opentelemetry/trace/propagation/trace_context.lua",
39 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
40 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
41 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
42 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
43 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
44 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
45 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
46 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
47 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
48 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
49 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
50 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
51 | }
52 | }
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.1-2.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.1-2"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | tag = "v0.1.2"
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "api7-lua-resty-http = 0.2.0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.context_storage"] = "lib/opentelemetry/context_storage.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
32 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
33 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
34 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
35 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
36 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
37 | ["opentelemetry.trace.propagation.carrier"] = "lib/opentelemetry/trace/propagation/carrier.lua",
38 | ["opentelemetry.trace.propagation.trace_context"] = "lib/opentelemetry/trace/propagation/trace_context.lua",
39 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
40 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
41 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
42 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
43 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
44 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
45 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
46 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
47 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
48 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
49 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
50 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
51 | }
52 | }
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.1-3.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.1-3"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | tag = "v0.1.3"
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "api7-lua-resty-http = 0.2.0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.context_storage"] = "lib/opentelemetry/context_storage.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
32 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
33 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
34 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
35 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
36 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
37 | ["opentelemetry.trace.propagation.carrier"] = "lib/opentelemetry/trace/propagation/carrier.lua",
38 | ["opentelemetry.trace.propagation.trace_context"] = "lib/opentelemetry/trace/propagation/trace_context.lua",
39 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
40 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
41 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
42 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
43 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
44 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
45 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
46 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
47 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
48 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
49 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
50 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
51 | }
52 | }
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/exporter/encoder.lua:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | -- The encoder is responsible for taking spans and serializing them into
3 | -- different export formats.
4 | -------------------------------------------------------------------------------
5 |
6 | local util = require("opentelemetry.util")
7 |
8 | local _M = {
9 | }
10 |
11 | local mt = {
12 | __index = _M
13 | }
14 |
15 | --------------------------------------------------------------------------------
16 | -- hex2bytes converts a hex string into bytes (used for transit over OTLP).
17 | --
18 | -- @param str Hex string.
19 | -- @return Table to be used as basis for more specific exporters.
20 | --------------------------------------------------------------------------------
21 | local function hex2bytes(str)
22 | return (str:gsub('..', function(cc)
23 | local n = tonumber(cc, 16)
24 | if n then
25 | return string.char(n)
26 | end
27 | end))
28 | end
29 |
30 | --------------------------------------------------------------------------------
31 | -- for_export structures span data for export; used as basis for more specific
32 | -- exporters.
33 | --
34 | -- @param span The span to export
35 | -- @return Table to be used as basis for more specific exporters.
36 | --------------------------------------------------------------------------------
37 | function _M.for_export(span)
38 | return {
39 | trace_id = span.ctx.trace_id,
40 | span_id = span.ctx.span_id,
41 | trace_state = span.ctx.trace_state:as_string(),
42 | parent_span_id = span.parent_ctx.span_id or "",
43 | name = span.name,
44 | kind = span.kind,
45 | start_time_unix_nano = string.format("%d", span.start_time),
46 | end_time_unix_nano = string.format("%d", span.end_time),
47 | attributes = span.attributes,
48 | dropped_attributes_count = 0,
49 | events = span.events,
50 | dropped_events_count = 0,
51 | links = {},
52 | dropped_links_count = 0,
53 | status = span.status
54 | }
55 | end
56 |
57 | --------------------------------------------------------------------------------
58 | -- for_otlp returns a table that can be protobuf-encoded for transmission over
59 | -- OTLP.
60 | --
61 | -- @param span The span to export
62 | -- @return Table to be protobuf-encoded
63 | --------------------------------------------------------------------------------
64 | function _M.for_otlp(span)
65 | local ret = _M.for_export(span)
66 | ret.trace_id = hex2bytes(ret.trace_id)
67 | ret.span_id = hex2bytes(ret.span_id)
68 | ret.parent_span_id = hex2bytes(ret.parent_span_id)
69 | return ret
70 | end
71 |
72 | --------------------------------------------------------------------------------
73 | -- for_console renders a string representation of span for console output.
74 | --
75 | -- @param span The span to export
76 | -- @return String representation of span.
77 | --------------------------------------------------------------------------------
78 | function _M.for_console(span)
79 | local ret = "\n---------------------------------------------------------\n"
80 | local ex = _M.for_export(span)
81 | ex["resource_attributes"] = span.tracer.provider.resource.attrs
82 | ret = ret .. util.table_as_string(ex, 2)
83 | ret = ret .. "---------------------------------------------------------\n"
84 | return ret
85 | end
86 |
87 | return _M
88 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/database.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.database
5 | local _M = {
6 | -- An identifier for the database management system (DBMS) product being used. See below for a list of well-known identifiers.
7 | DB_SYSTEM = "db.system",
8 | -- The connection string used to connect to the database. It is recommended to remove embedded credentials.
9 | DB_CONNECTION_STRING = "db.connection_string",
10 | -- Username for accessing the database.
11 | DB_USER = "db.user",
12 | -- The fully-qualified class name of the [Java Database Connectivity (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver used to connect.
13 | DB_JDBC_DRIVER_CLASSNAME = "db.jdbc.driver_classname",
14 | -- This attribute is used to report the name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails).
15 | DB_NAME = "db.name",
16 | -- The database statement being executed.
17 | DB_STATEMENT = "db.statement",
18 | -- The name of the operation being executed, e.g. the [MongoDB command name](https://docs.mongodb.com/manual/reference/command/#database-operations) such as `findAndModify`, or the SQL keyword.
19 | DB_OPERATION = "db.operation",
20 | -- The Microsoft SQL Server [instance name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) connecting to. This name is used to determine the port of a named instance.
21 | DB_MSSQL_INSTANCE_NAME = "db.mssql.instance_name",
22 | -- The fetch size used for paging, i.e. how many rows will be returned at once.
23 | DB_CASSANDRA_PAGE_SIZE = "db.cassandra.page_size",
24 | -- The consistency level of the query. Based on consistency values from [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html).
25 | DB_CASSANDRA_CONSISTENCY_LEVEL = "db.cassandra.consistency_level",
26 | -- The name of the primary table that the operation is acting upon, including the keyspace name (if applicable).
27 | DB_CASSANDRA_TABLE = "db.cassandra.table",
28 | -- Whether or not the query is idempotent.
29 | DB_CASSANDRA_IDEMPOTENCE = "db.cassandra.idempotence",
30 | -- The number of times a query was speculatively executed. Not set or `0` if the query was not executed speculatively.
31 | DB_CASSANDRA_SPECULATIVE_EXECUTION_COUNT = "db.cassandra.speculative_execution_count",
32 | -- The ID of the coordinating node for a query.
33 | DB_CASSANDRA_COORDINATOR_ID = "db.cassandra.coordinator.id",
34 | -- The data center of the coordinating node for a query.
35 | DB_CASSANDRA_COORDINATOR_DC = "db.cassandra.coordinator.dc",
36 | -- The index of the database being accessed as used in the [`SELECT` command](https://redis.io/commands/select), provided as an integer. To be used instead of the generic `db.name` attribute.
37 | DB_REDIS_DATABASE_INDEX = "db.redis.database_index",
38 | -- The collection being accessed within the database stated in `db.name`.
39 | DB_MONGODB_COLLECTION = "db.mongodb.collection",
40 | -- The name of the primary table that the operation is acting upon, including the database name (if applicable).
41 | DB_SQL_TABLE = "db.sql.table"
42 | }
43 | return _M
44 |
--------------------------------------------------------------------------------
/examples/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "io/ioutil"
6 | "log"
7 | "net/http"
8 | "os"
9 |
10 | "go.opentelemetry.io/otel"
11 | "go.opentelemetry.io/otel/codes"
12 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
13 | "go.opentelemetry.io/otel/propagation"
14 | "go.opentelemetry.io/otel/sdk/resource"
15 | sdktrace "go.opentelemetry.io/otel/sdk/trace"
16 | semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
17 | "go.opentelemetry.io/otel/trace"
18 | )
19 |
20 | // Initializes an OTLP exporter, and configures the corresponding trace and
21 | // metric providers.
22 | func initProvider() func() {
23 | ctx := context.Background()
24 |
25 | res, err := resource.New(ctx,
26 | resource.WithAttributes(
27 | // the service name used to display traces in backends
28 | semconv.ServiceNameKey.String("test-client"),
29 | ),
30 | )
31 | handleErr(err, "failed to create resource")
32 |
33 | // If the OpenTelemetry Collector is running on a local cluster (minikube or
34 | // microk8s), it should be accessible through the NodePort service at the
35 | // `localhost:30080` endpoint. Otherwise, replace `localhost` with the
36 | // endpoint of your cluster. If you run the app inside k8s, then you can
37 | // probably connect directly to the service through dns
38 |
39 | // Set up a trace exporter
40 | traceExporter, err := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("otel-collector:4317"), otlptracehttp.WithInsecure(),
41 | otlptracehttp.WithHeaders(map[string]string{
42 | "Content-Type": "application/json",
43 | }))
44 | handleErr(err, "failed to create trace exporter")
45 |
46 | // Register the trace exporter with a TracerProvider, using a batch
47 | // span processor to aggregate spans before export.
48 | bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
49 | tracerProvider := sdktrace.NewTracerProvider(
50 | sdktrace.WithSampler(sdktrace.AlwaysSample()),
51 | sdktrace.WithResource(res),
52 | sdktrace.WithSpanProcessor(bsp),
53 | )
54 | otel.SetTracerProvider(tracerProvider)
55 |
56 | // set global propagator to tracecontext (the default is no-op).
57 | otel.SetTextMapPropagator(propagation.TraceContext{})
58 |
59 | return func() {
60 | // Shutdown will flush any remaining spans and shut down the exporter.
61 | handleErr(tracerProvider.Shutdown(ctx), "failed to shutdown TracerProvider")
62 | }
63 | }
64 |
65 | func main() {
66 | shutdown := initProvider()
67 | defer shutdown()
68 |
69 | tracer := otel.Tracer("test-client-tracer")
70 | traceState, err := trace.ParseTraceState("trace-state-example-key=trace-state-example-val")
71 | handleErr(err, "parse trace state failed")
72 | ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
73 | TraceState: traceState,
74 | }))
75 | ctx, span := tracer.Start(ctx, "test-client-span", trace.WithSpanKind(trace.SpanKindClient))
76 | defer span.End()
77 |
78 | span.SetStatus(codes.Error, "client side failed")
79 |
80 | req, err := http.NewRequest("GET", os.Getenv("PROXY_ENDPOINT"), nil)
81 | handleErr(err, "new request fail")
82 |
83 | propagation.TraceContext{}.Inject(ctx, propagation.HeaderCarrier(req.Header))
84 | rsp, err := http.DefaultClient.Do(req)
85 | handleErr(err, "request fail")
86 |
87 | body, err := ioutil.ReadAll(rsp.Body)
88 | handleErr(err, "read body fail")
89 |
90 | log.Println(string(body))
91 | _ = rsp.Body.Close()
92 | }
93 |
94 | func handleErr(err error, message string) {
95 | if err != nil {
96 | log.Fatalf("%s: %v", message, err)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/spec/trace/propagation/text_map/composite_propagator_spec.lua:
--------------------------------------------------------------------------------
1 | local baggage = require "opentelemetry.baggage"
2 | local tracer_provider = require "opentelemetry.trace.tracer_provider"
3 | local composite_propagator = require "opentelemetry.trace.propagation.text_map.composite_propagator"
4 | local text_map_propagator = require "opentelemetry.trace.propagation.text_map.trace_context_propagator"
5 | local baggage_propagator = require "opentelemetry.baggage.propagation.text_map.baggage_propagator"
6 | local noop_propagator = require "opentelemetry.trace.propagation.text_map.noop_propagator"
7 | local context = require("opentelemetry.context")
8 |
9 | -- We're setting these on ngx.req but we aren't running openresty in these
10 | -- tests, so we'll mock that out (ngx.req supports get_headers() and set_header(header_name))
11 | local function newCarrier(headers_table)
12 | local r = { headers = {} }
13 | r.headers = headers_table
14 | r.get_headers = function() return r.headers end
15 | r.set_header = function(name, val) r.headers[name] = val end
16 | return r
17 | end
18 |
19 | -- We'll need to add more propagators to the repo (Jaeger, B3, etc), in order to
20 | -- fully test this.
21 | describe("composite propagator", function()
22 | describe(".inject", function()
23 | local tmp = text_map_propagator.new()
24 | local bp = baggage_propagator.new()
25 | local np = noop_propagator.new()
26 | local cp = composite_propagator.new({ tmp, bp, np })
27 | local ctx = context.new()
28 | local tracer_provider = tracer_provider.new()
29 | local tracer = tracer_provider:tracer("test tracer")
30 |
31 | -- start a trace
32 | local new_ctx = tracer:start(ctx, "span", { kind = 1 })
33 | local bgg = baggage.new({ foo = { value = "bar", metadata = "ok" } })
34 | new_ctx = new_ctx:inject_baggage(bgg)
35 |
36 | -- figure out what traceheader should look like
37 | local span_context = new_ctx.sp:context()
38 | local traceparent = string.format("00-%s-%s-%02x",
39 | span_context.trace_id,
40 | span_context.span_id,
41 | span_context.trace_flags)
42 | local carrier = newCarrier({})
43 |
44 | it("should add headers for each propagator", function()
45 | cp:inject(new_ctx, carrier)
46 | assert.are.same(
47 | carrier.get_headers()["traceparent"],
48 | traceparent
49 | )
50 | assert.are.same(
51 | carrier.get_headers()["baggage"],
52 | "foo=bar;ok"
53 | )
54 | end)
55 | end)
56 |
57 | describe(".extract", function()
58 | it("should extract headers for each propagator", function()
59 | local tmp = text_map_propagator.new()
60 | local bp = baggage_propagator.new()
61 | local np = noop_propagator.new()
62 | local cp = composite_propagator.new({ tmp, bp, np })
63 | local trace_id = "10f5b3bcfe3f0c2c5e1ef150fe0b5872"
64 | local carrier = newCarrier(
65 | { traceparent = string.format("00-%s-172accbce5f048db-01", trace_id),
66 | baggage = "foo=bar;ok" })
67 | local ctx = context.new()
68 | local new_ctx = cp:extract(ctx, carrier)
69 | assert.are.same(new_ctx.sp:context().trace_id, trace_id)
70 | assert.are.same(new_ctx:extract_baggage():get_value("foo"), "bar")
71 | end)
72 | end)
73 | end)
74 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-0.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-0"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | tag = "v0.2.0"
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.context_storage"] = "lib/opentelemetry/context_storage.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
32 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
33 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
34 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
35 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
36 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
37 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
38 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
39 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
40 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
41 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
42 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
43 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
44 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
45 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
46 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
47 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
48 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
49 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
50 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
51 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
52 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
53 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/tracer_provider.lua:
--------------------------------------------------------------------------------
1 | local instrumentation_library_new = require("opentelemetry.instrumentation_library").new
2 | local resource = require("opentelemetry.resource")
3 | local attr = require("opentelemetry.attribute")
4 | local tracer_new = require("opentelemetry.trace.tracer").new
5 | local id_generator = require("opentelemetry.trace.id_generator")
6 | local always_on_sampler_new = require("opentelemetry.trace.sampling.always_on_sampler").new
7 | local parent_base_sampler_new = require("opentelemetry.trace.sampling.parent_base_sampler").new
8 |
9 | local _M = {
10 | }
11 |
12 | local mt = {
13 | __index = _M
14 | }
15 |
16 | ------------------------------------------------------------------
17 | -- create a tracer provider.
18 | --
19 | -- @span_processor [optional] span_processors. Should be table
20 | -- but we allow for a single span_processor
21 | -- for backwards compatibility.
22 | -- @opts [optional] config
23 | -- opts.sampler: opentelemetry.trace.sampling.*, default parent_base_sampler
24 | -- opts.resource
25 | -- @return tracer provider factory
26 | ------------------------------------------------------------------
27 | function _M.new(span_processors, opts)
28 | if not opts then
29 | opts = {}
30 | end
31 |
32 | -- Handle nil span processors and users that pass single
33 | -- span processor not wrapped in a table.
34 | if span_processors and #span_processors == 0 then
35 | span_processors = { span_processors }
36 | elseif span_processors == nil then
37 | span_processors = {}
38 | end
39 |
40 | local r = resource.new(attr.string("telemetry.sdk.language", "lua"),
41 | attr.string("telemetry.sdk.name", "opentelemetry-lua"),
42 | attr.string("telemetry.sdk.version", "0.1.1"))
43 |
44 | local self = {
45 | span_processors = span_processors,
46 | sampler = opts.sampler or parent_base_sampler_new(always_on_sampler_new()),
47 | resource = resource.merge(opts.resource, r),
48 | id_generator = id_generator,
49 | named_tracer = {},
50 | }
51 | return setmetatable(self, mt)
52 | end
53 |
54 | ------------------------------------------------------------------
55 | -- create a tracer.
56 | --
57 | -- @name tracer name
58 | -- @opts [optional] config
59 | -- opts.version: specifies the version of the instrumentation library
60 | -- opts.schema_url: specifies the Schema URL that should be recorded in the emitted telemetry.
61 | -- @return tracer
62 | ------------------------------------------------------------------
63 | function _M.tracer(self, name, opts)
64 | if not opts then
65 | opts = {
66 | version = "",
67 | schema_url = "",
68 | }
69 | end
70 | local key = name .. opts.version .. opts.schema_url
71 | local tracer = self.named_tracer[key]
72 | if tracer then
73 | return tracer
74 | end
75 |
76 | self.named_tracer[key] = tracer_new(self, instrumentation_library_new(name, opts.version, opts.schema_url))
77 | return self.named_tracer[key]
78 | end
79 |
80 | function _M.force_flush(self)
81 | for _, sp in ipairs(self.span_processors) do
82 | sp:force_flush()
83 | end
84 | end
85 |
86 | function _M.shutdown(self)
87 | for _, sp in ipairs(self.span_processors) do
88 | sp:shutdown()
89 | end
90 | end
91 |
92 | function _M.register_span_processor(self, sp)
93 | table.insert(self.span_processors, sp)
94 | end
95 |
96 | function _M.set_span_processors(self, ...)
97 | self.span_processors = { ... }
98 | end
99 |
100 | return _M
101 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | concurrency:
4 | group: ${{ github.ref }}
5 | cancel-in-progress: true
6 |
7 | on:
8 | push:
9 | branches:
10 | - "main"
11 | - "update-ci"
12 | pull_request:
13 | branches:
14 | - main
15 |
16 | jobs:
17 | build-openresty:
18 | name: "Build openresty image"
19 | runs-on: ubuntu-20.04
20 | steps:
21 | - uses: actions/checkout@v3
22 | - uses: docker/setup-buildx-action@v1
23 | - uses: docker/build-push-action@v4
24 | with:
25 | context: .
26 | cache-from: type=gha
27 | cache-to: type=gha,mode=max
28 | push: false
29 | outputs: type=docker,dest=/tmp/openresty-image.tar
30 | tags: opentelemetry-lua_openresty
31 | - name: Upload artifact
32 | uses: actions/upload-artifact@v2
33 | with:
34 | name: openresty-image
35 | path: /tmp/openresty-image.tar
36 | end-to-end-tests:
37 | name: "End to end tests"
38 | timeout-minutes: 60
39 | runs-on: ubuntu-latest
40 | needs:
41 | - build-openresty
42 | steps:
43 | - uses: actions/checkout@v3
44 | - uses: docker/setup-buildx-action@v1
45 | - uses: docker/build-push-action@v4
46 | with:
47 | context: examples/server
48 | cache-from: type=gha
49 | cache-to: type=gha,mode=max
50 | push: false
51 | tags: opentelemetry-lua_test-server
52 | - uses: docker/build-push-action@v4
53 | with:
54 | context: examples/client
55 | cache-from: type=gha
56 | cache-to: type=gha,mode=max
57 | push: false
58 | tags: opentelemetry-lua_test-client
59 | - name: Download artifact
60 | uses: actions/download-artifact@v2
61 | with:
62 | name: openresty-image
63 | path: /tmp
64 | - name: Load Docker images
65 | run: |
66 | docker load --input /tmp/openresty-image.tar
67 | docker image ls -a
68 | - name: Start containers
69 | run: make openresty-dev CONTAINER_ORCHESTRATOR_EXEC_OPTIONS="-T"
70 | - name: Run openresty integration tests
71 | run: make openresty-integration-test CONTAINER_ORCHESTRATOR_EXEC_OPTIONS="-T"
72 | - name: Run openresty-test-e2e-trace-context
73 | run: make openresty-test-e2e-trace-context CONTAINER_ORCHESTRATOR_EXEC_OPTIONS="-T"
74 | - name: Run openresty-test-e2e
75 | run: make openresty-test-e2e CONTAINER_ORCHESTRATOR_EXEC_OPTIONS="-T"
76 |
77 | unit-tests:
78 | name: "unit tests"
79 | timeout-minutes: 60
80 | runs-on: ubuntu-latest
81 | needs:
82 | - build-openresty
83 | steps:
84 | - name: Checkout
85 | uses: actions/checkout@v2
86 | - name: Set up Docker Buildx
87 | uses: docker/setup-buildx-action@v1
88 | - name: Download artifact
89 | uses: actions/download-artifact@v2
90 | with:
91 | name: openresty-image
92 | path: /tmp
93 | - name: Load Docker images
94 | run: |
95 | docker load --input /tmp/openresty-image.tar
96 | docker image ls -a
97 | - name: Run busted unit tests
98 | run: make lua-unit-test
99 | - name: Run api tests
100 | run: make api-test
101 | book-keeping:
102 | name: "Book-keeping"
103 | runs-on: ubuntu-20.04
104 | steps:
105 | - uses: actions/checkout@v3
106 | - uses: docker/setup-buildx-action@v1
107 | - uses: docker/build-push-action@v4
108 | with:
109 | context: utils
110 | cache-from: type=gha
111 | cache-to: type=gha,mode=max
112 | push: false
113 | tags: opentelemetry-lua_utils
114 | - run: make check-format
115 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/http.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.http
5 | local attribute = require("opentelemetry.attribute")
6 |
7 | local _M = {
8 | -- HTTP request method.
9 | HTTP_METHOD = "http.method",
10 | -- [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6).
11 | HTTP_STATUS_CODE = "http.status_code",
12 | -- Kind of HTTP protocol used.
13 | HTTP_FLAVOR = "http.flavor",
14 | -- Value of the [HTTP User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) header sent by the client.
15 | HTTP_USER_AGENT = "http.user_agent",
16 | -- The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size.
17 | HTTP_REQUEST_CONTENT_LENGTH = "http.request_content_length",
18 | -- The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size.
19 | HTTP_RESPONSE_CONTENT_LENGTH = "http.response_content_length",
20 | -- Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not transmitted over HTTP, but if it is known, it should be included nevertheless.
21 | HTTP_URL = "http.url",
22 | -- The ordinal number of request resending attempt (for any reason, including redirects).
23 | HTTP_RESEND_COUNT = "http.resend_count",
24 | -- The URI scheme identifying the used protocol.
25 | HTTP_SCHEME = "http.scheme",
26 | -- The full request target as passed in a HTTP request line or equivalent.
27 | HTTP_TARGET = "http.target",
28 | -- The matched route (path template in the format used by the respective server framework). See note below
29 | HTTP_ROUTE = "http.route",
30 | -- The IP address of the original client behind all proxies, if known (e.g. from [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)).
31 | HTTP_CLIENT_IP = "http.client_ip",
32 | -- The opentelemetry attribute key prefix for HTTP request headers
33 | HTTP_REQUEST_HEADER = "http.request.header",
34 | -- The opentelemetry attribute key prefix for HTTP response headers
35 | HTTP_RESPONSE_HEADER = "http.response.header",
36 | }
37 |
38 | ------------------------------------------------------------------
39 | -- returns the contents of headers as OpenTelemetry attributes.
40 | --
41 | -- @headers a table of HTTP request headers
42 | -- @return a table of attribute
43 | ------------------------------------------------------------------
44 | function _M.request_header(headers)
45 | local attributes = {}
46 | for k, v in pairs(headers) do
47 | k = _M.HTTP_REQUEST_HEADER .. '.' .. k
48 | if type(v) == "table" then
49 | table.insert(attributes, attribute.string_array(k, v))
50 | else
51 | table.insert(attributes, attribute.string(k, v))
52 | end
53 | end
54 | return attributes
55 | end
56 |
57 | ------------------------------------------------------------------
58 | -- returns the contents of headers as OpenTelemetry attributes.
59 | --
60 | -- @headers a table of HTTP response headers
61 | -- @return a table of attribute
62 | ------------------------------------------------------------------
63 | function _M.response_header(headers)
64 | local attributes = {}
65 | for k, v in pairs(headers) do
66 | k = _M.HTTP_RESPONSE_HEADER .. '.' .. k
67 | if type(v) == "table" then
68 | table.insert(attributes, attribute.string_array(k, v))
69 | else
70 | table.insert(attributes, attribute.string(k, v))
71 | end
72 | end
73 | return attributes
74 | end
75 |
76 | return _M
77 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-1"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.1",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
25 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
26 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
27 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
32 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
33 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
34 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
35 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
36 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
37 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
38 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
39 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
40 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
41 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
42 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
43 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
44 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
45 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
46 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
47 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
48 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
49 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
50 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
51 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
52 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
53 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
54 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
55 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
56 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
57 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
58 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/spec/context_spec.lua:
--------------------------------------------------------------------------------
1 | local context = require("opentelemetry.context")
2 | local otel_global = require("opentelemetry.global")
3 | local baggage = require("opentelemetry.baggage")
4 |
5 | describe("get and set", function()
6 | it("stores and retrieves values at given key", function()
7 | local ctx = context.new()
8 | local new_ctx = ctx:set("key", "value")
9 | assert.are.equal(new_ctx:get("key"), "value")
10 | assert.are_not.equal(ctx, new_ctx)
11 | end)
12 | end)
13 |
14 | describe("current", function()
15 | it("returns last element stored in stack at context_key", function()
16 | local ctx_1 = context.new({ foo = "bar"})
17 | local ctx_2 = context.new({ foo = "baz"})
18 | local ctx_storage = { __opentelemetry_context__ = {ctx_1, ctx_2} }
19 | otel_global.set_context_storage(ctx_storage)
20 | assert.are.equal(ctx_2, context.current())
21 | end)
22 |
23 | it("instantiates different noop spans when no span provided", function()
24 | local ctx_1 = context.new()
25 | local ctx_2 = context.new()
26 |
27 | -- accessing span context on different contexts gives different object back
28 | assert.are_not.equal(ctx_1.sp:context(), ctx_2.sp:context())
29 | assert.are_not.equal(ctx_1.sp:context().trace_state, ctx_2.sp:context().trace_state)
30 |
31 | -- accessing span context on same context gives same object back
32 | assert.are.equal(ctx_1.sp:context(), ctx_1.sp:context())
33 | end)
34 | end)
35 |
36 | describe("with_span", function()
37 | it("sets supplied entries on new context", function()
38 | otel_global.set_context_storage({})
39 | local original_entries = { foo = "bar" }
40 | local old_ctx = context.new(original_entries, "oldspan")
41 | local ctx = old_ctx:with_span("myspan")
42 | assert.are.same(ctx.entries, original_entries)
43 | end)
44 |
45 | it("handles absence of entries arg gracefully", function()
46 | local fake_span = "hi"
47 | local ctx = context:with_span(fake_span)
48 | assert.are.same(ctx.entries, {})
49 | end)
50 | end)
51 |
52 | describe("attach", function()
53 | it("creates new table at context_key if no table present and returns token matching length of stack after adding element", function()
54 | local ctx_storage = {}
55 | otel_global.set_context_storage(ctx_storage)
56 | local ctx = context.new({ foo = "bar"})
57 | local token = ctx:attach()
58 | assert.are.equal(token, 1)
59 | end)
60 |
61 | it("appends to existing table at context_key", function()
62 | local ctx_storage = {}
63 | otel_global.set_context_storage(ctx_storage)
64 | local ctx_1 = context.new({ foo = "bar"})
65 | local ctx_2 = context.new({ foo = "baz"})
66 | local token_1 = ctx_1:attach()
67 | local token_2 = ctx_2:attach()
68 | assert.are.equal(token_1, 1)
69 | assert.are.equal(token_2, 2)
70 | end)
71 | end)
72 |
73 | describe("detach", function()
74 | it("removes final context from stack at context_key", function()
75 | local ctx_storage = {}
76 | otel_global.set_context_storage(ctx_storage)
77 | local ctx = context.new()
78 | local token = ctx:attach()
79 | local outcome, err = ctx:detach(token)
80 | assert.is_true(outcome)
81 | assert.is_nil(err)
82 | end)
83 |
84 | it("returns outcome of 'false' and error string if token does not match", function()
85 | -- Swallow logs since we are expecting them
86 | stub(ngx, "log")
87 | local ctx_storage = {}
88 | otel_global.set_context_storage(ctx_storage)
89 | local ctx = context.new()
90 | ctx:attach()
91 | local outcome, err = ctx:detach(2)
92 | ngx.log:revert()
93 | assert.is_false(outcome)
94 | assert.is_same("Token does not match (1 context entries in stack, token provided was 2).", err)
95 | end)
96 | end)
97 |
98 | describe("inject and extract baggage", function()
99 | it("adds baggage to context and extracts it", function()
100 | local ctx = context.new(storage)
101 | local baggage = baggage.new({ key1 = { value = "wat" } })
102 | local new_ctx = ctx:inject_baggage(baggage)
103 | assert.are.same(new_ctx:extract_baggage(), baggage)
104 | end)
105 | end)
106 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/exporter/circuit.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- Contains circuit for use in exporters. For more on the circuit breaker
3 | -- pattern, see https://martinfowler.com/bliki/CircuitBreaker.html.
4 | --------------------------------------------------------------------------------
5 | local util = require("opentelemetry.util")
6 | local otel_global = require("opentelemetry.global")
7 |
8 | local _M = {
9 | OPEN = "open",
10 | CLOSED = "closed",
11 | HALF_OPEN = "half-open"
12 | }
13 |
14 | local mt = {
15 | __index = _M
16 | }
17 |
18 | --------------------------------------------------------------------------------
19 | -- Returns a new circuit. No more than 1 request should be in flight at a time,
20 | -- when those requests are brokered by this circuit.
21 | --
22 | -- @param options Table containing two keys:
23 | -- failure_threshold: number of failures before the circuit opens and requests
24 | -- stop flowing
25 | -- reset_timeout_ms: time in to wait im ms before setting circuit to half-open
26 | --
27 | -- @return circuit instance
28 | --------------------------------------------------------------------------------
29 | function _M.new(options)
30 | options = options or {}
31 | local self = {
32 | reset_timeout_ms = options["reset_timeout_ms"] or 5000,
33 | failure_threshold = options["failure_threshold"] or 5,
34 | failure_count = 0,
35 | open_start_time_ms = nil,
36 | state = "closed",
37 | }
38 | return setmetatable(self, mt)
39 | end
40 |
41 | --------------------------------------------------------------------------------
42 | -- should_make_request determines if a request should be made or not. It assumes
43 | -- that circuit state is either CLOSED or OPEN at beginning of method, since the
44 | -- code assumes that the circuit only brokers one request at a time, and assumes
45 | -- that we only ever make one request in HALF_OPEN state before setting to OPEN
46 | -- or CLOSED.
47 | --
48 | -- @return boolean
49 | --------------------------------------------------------------------------------
50 | function _M.should_make_request(self)
51 | if self.state == self.CLOSED then
52 | return true
53 | end
54 |
55 | if self.state == self.OPEN then
56 | if (util.gettimeofday_ms() - self.open_start_time_ms) < self.reset_timeout_ms then
57 | return false
58 | else
59 | self.state = self.HALF_OPEN
60 | return true
61 | end
62 | end
63 |
64 | ngx.log(ngx.ERR, "Circuit breaker could not determine if request should be made (current state: " .. self.state)
65 | end
66 |
67 | --------------------------------------------------------------------------------
68 | -- record_failure does internal book-keeping about failures and resets circuit
69 | -- state accordingly.
70 | --
71 | -- @return nil
72 | --------------------------------------------------------------------------------
73 | function _M.record_failure(self)
74 | self.failure_count = self.failure_count + 1
75 |
76 | if self.state == self.CLOSED and self.failure_count >= self.failure_threshold then
77 | otel_global.metrics_reporter:add_to_counter(
78 | "otel.bsp.circuit_breaker_opened", 1)
79 | self.state = self.OPEN
80 | self.open_start_time_ms = util.gettimeofday_ms()
81 | end
82 |
83 | if self.state == self.HALF_OPEN then
84 | otel_global.metrics_reporter:add_to_counter(
85 | "otel.bsp.circuit_breaker_opened", 1)
86 | self.state = self.OPEN
87 | self.open_start_time_ms = util.gettimeofday_ms()
88 | end
89 | end
90 |
91 | --------------------------------------------------------------------------------
92 | -- record_success does internal book-keeping about successful requests and
93 | -- resets circuit state accordingly.
94 | --
95 | -- @return nil
96 | --------------------------------------------------------------------------------
97 | function _M.record_success(self)
98 | if self.state == self.CLOSED then
99 | return
100 | end
101 |
102 | if self.state == self.HALF_OPEN then
103 | otel_global.metrics_reporter:add_to_counter(
104 | "otel.bsp.circuit_breaker_closed", 1)
105 | self.failure_count = 0
106 | self.state = self.CLOSED
107 | return
108 | end
109 | end
110 |
111 | return _M
112 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-2.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-2"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.2",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
23 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
24 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
25 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
26 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
27 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
28 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
29 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
30 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
31 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
32 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
33 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
34 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
35 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
36 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
37 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
38 | ["opentelemetry.trace.simple_span_processor"] = "lib/opentelemetry/trace/simple_span_processor.lua",
39 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
40 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
41 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
42 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
43 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
44 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
45 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
46 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
47 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
48 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
49 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
50 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
51 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
52 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
53 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
54 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
55 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
56 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
57 | ["opentelemetry.trace.tracestate"] = "lib/opentelemetry/trace/tracestate.lua",
58 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
59 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
60 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua:
--------------------------------------------------------------------------------
1 | local span_context = require("opentelemetry.trace.span_context")
2 | local tracestate = require("opentelemetry.trace.tracestate")
3 | local text_map_getter = require("opentelemetry.trace.propagation.text_map.getter")
4 | local text_map_setter = require("opentelemetry.trace.propagation.text_map.setter")
5 | local util = require("opentelemetry.util")
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M,
12 | }
13 |
14 | -- these should be constants, but they were not supported until Lua 5.4, and
15 | -- LuaJIT (which openresty runs on) works up to Lua 5.2
16 | local traceparent_header = "traceparent"
17 | local tracestate_header = "tracestate"
18 |
19 | local invalid_trace_id = span_context.INVALID_TRACE_ID
20 | local invalid_span_id = span_context.INVALID_SPAN_ID
21 |
22 | function _M.new()
23 | return setmetatable(
24 | {
25 | text_map_setter = text_map_setter.new(),
26 | text_map_getter = text_map_getter.new()
27 | }, mt)
28 | end
29 |
30 | ------------------------------------------------------------------
31 | -- Add tracing information to nginx request as headers
32 | --
33 | -- @param context context storage
34 | -- @param carrier nginx request
35 | -- @param setter setter for interacting with carrier
36 | -- @return nil
37 | ------------------------------------------------------------------
38 | function _M:inject(context, carrier, setter)
39 | setter = setter or self.text_map_setter
40 | local span_context = context:span_context()
41 | if not span_context:is_valid() then
42 | return
43 | end
44 | local traceparent = string.format("00-%s-%s-%02x",
45 | span_context.trace_id, span_context.span_id, span_context.trace_flags)
46 | setter.set(carrier, traceparent_header, traceparent)
47 | if span_context.trace_state then
48 | setter.set(carrier, tracestate_header, span_context.trace_state:as_string())
49 | end
50 | end
51 |
52 | local function validate_trace_id(trace_id)
53 | return type(trace_id) == "string" and #trace_id == 32 and trace_id ~= invalid_trace_id
54 | and string.match(trace_id, "^[0-9a-f]+$")
55 | end
56 |
57 | local function validate_span_id(span_id)
58 | return type(span_id) == "string" and #span_id == 16 and span_id ~= invalid_span_id
59 | and string.match(span_id, "^[0-9a-f]+$")
60 | end
61 |
62 | -- Traceparent: 00-982d663bad6540dece76baf15dd2aa7f-6827812babd449d1-01
63 | -- version-trace-id-parent-id-trace-flags
64 | local function parse_trace_parent(trace_parent)
65 | if type(trace_parent) ~= "string" or trace_parent == "" then
66 | return
67 | end
68 | local ret = util.split(util.trim(trace_parent), "-")
69 | if #ret < 4 then
70 | return
71 | end
72 |
73 | if #ret[1] ~= 2 then
74 | return
75 | end
76 |
77 | local version = tonumber(ret[1], 16)
78 | if not version or version > 254 then
79 | return
80 | end
81 | if version == 0 and #ret ~= 4 then
82 | return
83 | end
84 |
85 | if not validate_trace_id(ret[2]) then
86 | return
87 | end
88 |
89 | if not validate_span_id(ret[3]) then
90 | return
91 | end
92 |
93 | if #ret[4] ~= 2 then
94 | return
95 | end
96 |
97 | local trace_flags = tonumber(ret[4], 16)
98 | if not trace_flags or trace_flags > 2 then
99 | return
100 | end
101 |
102 | return ret[2], ret[3], trace_flags
103 | end
104 |
105 | ------------------------------------------------------------------
106 | -- extract span context from upstream request.
107 | --
108 | -- @context current context
109 | -- @carrier get traceparent and tracestate
110 | -- @return new context
111 | ------------------------------------------------------------------
112 | function _M:extract(context, carrier, getter)
113 | getter = getter or self.text_map_getter
114 | local trace_id, span_id, trace_flags = parse_trace_parent(getter.get(carrier, traceparent_header))
115 | if not trace_id or not span_id or not trace_flags then
116 | return context
117 | end
118 |
119 | local trace_state = tracestate.parse_tracestate(getter.get(carrier, tracestate_header))
120 |
121 | return context:with_span_context(span_context.new(trace_id, span_id, trace_flags, trace_state, true))
122 | end
123 |
124 | function _M.fields()
125 | return { "traceparent", "tracestate" }
126 | end
127 |
128 | return _M
129 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-3.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-3"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.3",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.api.trace.span_status"] = "lib/opentelemetry/api/trace/span_status.lua",
23 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
24 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
29 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
30 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
31 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
32 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
33 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
34 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
35 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
36 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
37 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
38 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
39 | ["opentelemetry.trace.simple_span_processor"] = "lib/opentelemetry/trace/simple_span_processor.lua",
40 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
41 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
42 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
43 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
44 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
45 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
46 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
47 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
48 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
49 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
50 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
51 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
52 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
53 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
54 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
55 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
56 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
57 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
58 | ["opentelemetry.trace.tracestate"] = "lib/opentelemetry/trace/tracestate.lua",
59 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
60 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
61 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | --
3 | -- See https://w3c.github.io/baggage/ for details.
4 | --
5 | --------------------------------------------------------------------------------
6 |
7 | local baggage = require("opentelemetry.baggage")
8 | local text_map_getter = require("opentelemetry.trace.propagation.text_map.getter")
9 | local text_map_setter = require("opentelemetry.trace.propagation.text_map.setter")
10 | local util = require("opentelemetry.util")
11 |
12 | local _M = {
13 | }
14 |
15 | local mt = {
16 | __index = _M,
17 | }
18 |
19 | local baggage_header = "baggage"
20 |
21 | function _M.new()
22 | return setmetatable(
23 | {
24 | text_map_setter = text_map_setter.new(),
25 | text_map_getter = text_map_getter.new()
26 | }, mt)
27 | end
28 |
29 | -------------=------------------------------------------------------------------
30 | -- Set baggage header on outbound HTTP request header.
31 | --
32 | -- @param context context storage
33 | -- @param carrier nginx request
34 | -- @param setter setter for interacting with carrier
35 | -- @return nil
36 | --------------------------------------------------------------------------------
37 | function _M:inject(context, carrier, setter)
38 | setter = setter or self.text_map_setter
39 | local bgg = context:extract_baggage()
40 | local header_string = ""
41 | for k, v in pairs(bgg.values) do
42 | local element = k .. "=" .. v.value
43 | if v.metadata then
44 | element = element .. ";" .. v.metadata
45 | end
46 | element = element .. ","
47 | header_string = header_string .. element
48 | end
49 |
50 | -- trim trailing comma
51 | header_string = header_string:sub(0, -2)
52 |
53 | setter.set(
54 | carrier,
55 | baggage_header,
56 | util.percent_encode_baggage_string(header_string)
57 | )
58 | end
59 |
60 | --------------------------------------------------------------------------------
61 | -- Extract baggage from HTTP request headers.
62 | --
63 | -- @context current context
64 | -- @carrier ngx.req
65 | -- @return new context with baggage associated
66 | --------------------------------------------------------------------------------
67 | function _M:extract(context, carrier, getter)
68 | getter = getter or self.text_map_getter
69 | local baggage_string = getter.get(carrier, baggage_header)
70 | if not baggage_string then
71 | return context
72 | else
73 | baggage_string = util.decode_percent_encoded_string(baggage_string)
74 | end
75 |
76 | local baggage_entries = {}
77 | -- split apart string on comma and build up baggage entries
78 | for list_member in string.gmatch(baggage_string, "([^,]+)") do
79 | -- extract metadata from each list member
80 | local kv, metadata = string.match(list_member, "([^;]+);(.*)")
81 |
82 | -- If there's no semicolon in list member, then kv and metadata are nil
83 | -- and we need to correct that
84 | if not kv then
85 | kv = list_member
86 | metadata = ""
87 | end
88 |
89 | -- split apart k/v on equals sign
90 | -- and remove leading and trailing whitespaces in k/v
91 | for k, v in string.gmatch(kv, "(%S.-)%s*=%s*(.*%S)") do
92 | if self.validate_baggage(k, v) then
93 | baggage_entries[k] = { value = v, metadata = metadata }
94 | else
95 | ngx.log(ngx.WARN, "invalid baggage entry: " .. k .. "=" .. v)
96 | end
97 | end
98 | end
99 |
100 | local extracted_baggage = baggage.new(baggage_entries)
101 | return context:inject_baggage(extracted_baggage)
102 | end
103 |
104 | --------------------------------------------------------------------------------
105 | -- Check to see if baggage has both key and value component
106 | --
107 | -- @key baggage key
108 | -- @value baggage value
109 | -- @return boolean
110 | --------------------------------------------------------------------------------
111 | function _M.validate_baggage(key, value)
112 | return (key and value) ~= nil
113 | end
114 |
115 | --------------------------------------------------------------------------------
116 | -- Fields that will be used by the propagator
117 | --
118 | -- @return table
119 | --------------------------------------------------------------------------------
120 | function _M.fields()
121 | return { "baggage" }
122 | end
123 |
124 | return _M
125 |
--------------------------------------------------------------------------------
/lib/opentelemetry/semantic_conventions/trace/general.lua:
--------------------------------------------------------------------------------
1 | --- This file was automatically generated by utils/generate_semantic_conventions.lua
2 | -- See: https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/trace/semantic_conventions
3 | --
4 | -- module @semantic_conventions.trace.general
5 | local _M = {
6 | -- Transport protocol used. See note below.
7 | NET_TRANSPORT = "net.transport",
8 | -- Application layer protocol used. The value SHOULD be normalized to lowercase.
9 | NET_APP_PROTOCOL_NAME = "net.app.protocol.name",
10 | -- Version of the application layer protocol used. See note below.
11 | NET_APP_PROTOCOL_VERSION = "net.app.protocol.version",
12 | -- Remote socket peer name.
13 | NET_SOCK_PEER_NAME = "net.sock.peer.name",
14 | -- Remote socket peer address: IPv4 or IPv6 for internet protocols, path for local communication, [etc](https://man7.org/linux/man-pages/man7/address_families.7.html).
15 | NET_SOCK_PEER_ADDR = "net.sock.peer.addr",
16 | -- Remote socket peer port.
17 | NET_SOCK_PEER_PORT = "net.sock.peer.port",
18 | -- Protocol [address family](https://man7.org/linux/man-pages/man7/address_families.7.html) which is used for communication.
19 | NET_SOCK_FAMILY = "net.sock.family",
20 | -- Logical remote hostname, see note below.
21 | NET_PEER_NAME = "net.peer.name",
22 | -- Logical remote port number
23 | NET_PEER_PORT = "net.peer.port",
24 | -- Logical local hostname or similar, see note below.
25 | NET_HOST_NAME = "net.host.name",
26 | -- Logical local port number, preferably the one that the peer used to connect
27 | NET_HOST_PORT = "net.host.port",
28 | -- Local socket address. Useful in case of a multi-IP host.
29 | NET_SOCK_HOST_ADDR = "net.sock.host.addr",
30 | -- Local socket port number.
31 | NET_SOCK_HOST_PORT = "net.sock.host.port",
32 | -- The internet connection type currently being used by the host.
33 | NET_HOST_CONNECTION_TYPE = "net.host.connection.type",
34 | -- This describes more details regarding the connection.type. It may be the type of cell technology connection, but it could be used for describing details about a wifi connection.
35 | NET_HOST_CONNECTION_SUBTYPE = "net.host.connection.subtype",
36 | -- The name of the mobile carrier.
37 | NET_HOST_CARRIER_NAME = "net.host.carrier.name",
38 | -- The mobile carrier country code.
39 | NET_HOST_CARRIER_MCC = "net.host.carrier.mcc",
40 | -- The mobile carrier network code.
41 | NET_HOST_CARRIER_MNC = "net.host.carrier.mnc",
42 | -- The ISO 3166-1 alpha-2 2-character country code associated with the mobile carrier network.
43 | NET_HOST_CARRIER_ICC = "net.host.carrier.icc",
44 | -- The [`service.name`](../../resource/semantic_conventions/README.md#service) of the remote service. SHOULD be equal to the actual `service.name` resource attribute of the remote service if any.
45 | PEER_SERVICE = "peer.service",
46 | -- Username or client_id extracted from the access token or [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in the inbound request from outside the system.
47 | ENDUSER_ID = "enduser.id",
48 | -- Actual/assumed role the client is making the request under extracted from token or application security context.
49 | ENDUSER_ROLE = "enduser.role",
50 | -- Scopes or granted authorities the client currently possesses extracted from token or application security context. The value would come from the scope associated with an [OAuth 2.0 Access Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute value in a [SAML 2.0 Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html).
51 | ENDUSER_SCOPE = "enduser.scope",
52 | -- Current "managed" thread ID (as opposed to OS thread ID).
53 | THREAD_ID = "thread.id",
54 | -- Current thread name.
55 | THREAD_NAME = "thread.name",
56 | -- The method or function name, or equivalent (usually rightmost part of the code unit's name).
57 | CODE_FUNCTION = "code.function",
58 | -- The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit.
59 | CODE_NAMESPACE = "code.namespace",
60 | -- The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path).
61 | CODE_FILEPATH = "code.filepath",
62 | -- The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`.
63 | CODE_LINENO = "code.lineno",
64 | -- The column number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`.
65 | CODE_COLUMN = "code.column"
66 | }
67 | return _M
68 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/recording_span.lua:
--------------------------------------------------------------------------------
1 | local attribute = require("opentelemetry.attribute")
2 | local event_new = require("opentelemetry.trace.event").new
3 | local span_kind = require("opentelemetry.trace.span_kind")
4 | local span_status = require("opentelemetry.trace.span_status")
5 | local util = require("opentelemetry.util")
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M
12 | }
13 |
14 | ------------------------------------------------------------------
15 | -- create a recording span.
16 | --
17 | -- @tracer
18 | -- @parent_ctx parent span context
19 | -- @ctx current span context
20 | -- @name span name
21 | -- @config optional config
22 | -- config.kind: span kind
23 | -- config.attributes: span attributes
24 | -- @start_time optional start time
25 | -- @return span
26 | ------------------------------------------------------------------
27 | function _M.new(tracer, parent_ctx, ctx, name, config, start_time)
28 | local self = {
29 | tracer = tracer,
30 | parent_ctx = parent_ctx,
31 | ctx = ctx,
32 | name = name,
33 | start_time = start_time or util.time_nano(),
34 | end_time = 0,
35 | kind = span_kind.validate(config.kind),
36 | attributes = config.attributes or {},
37 | events = {},
38 | }
39 | return setmetatable(self, mt)
40 | end
41 |
42 | function _M.context(self)
43 | return self.ctx
44 | end
45 |
46 | function _M.is_recording(self)
47 | return self.end_time == 0
48 | end
49 |
50 | ------------------------------------------------------------------
51 | -- set span status.
52 | --
53 | -- @code see opentelemetry.trace.span_status.*
54 | -- @message error msg
55 | ------------------------------------------------------------------
56 | function _M.set_status(self, code, message)
57 | if not self:is_recording() then
58 | return
59 | end
60 |
61 | code = span_status.validate(code)
62 | local status = {
63 | code = code,
64 | message = ""
65 | }
66 | if code == span_status.ERROR then
67 | status.message = message
68 | end
69 |
70 | self.status = status
71 | end
72 |
73 | function _M.set_attributes(self, ...)
74 | if not self:is_recording() then
75 | return
76 | end
77 |
78 | for _, attr in ipairs({ ... }) do
79 | table.insert(self.attributes, attr)
80 | end
81 | end
82 |
83 | ------------------------------------------------------------------
84 | -- Finish the span. `end` is key word, so we use "finish."
85 | -- See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#end
86 | --
87 | -- @end_timestamp end timestamp for span (nanoseconds). If not
88 | -- supplied, span method will end at current
89 | -- time.
90 | ------------------------------------------------------------------
91 | function _M.finish(self, end_timestamp)
92 | if not self:is_recording() then
93 | return
94 | end
95 |
96 | self.end_time = end_timestamp or util.time_nano()
97 | for _, sp in ipairs(self.tracer.provider.span_processors) do
98 | sp:on_end(self)
99 | end
100 | end
101 |
102 | ------------------------------------------------------------------
103 | -- add a error event
104 | --
105 | -- @error a string describe the error
106 | ------------------------------------------------------------------
107 | function _M.record_error(self, error)
108 | if error == nil or (type(error) ~= "string") or (not self:is_recording()) then
109 | return
110 | end
111 |
112 | table.insert(self.events, event_new("exception", {
113 | attributes = { attribute.string("exception.message", error) },
114 | }))
115 | end
116 |
117 | ------------------------------------------------------------------
118 | -- add a custom event
119 | --
120 | -- @opts [optional]
121 | -- opts.attributes: a list of attribute
122 | ------------------------------------------------------------------
123 | function _M.add_event(self, name, opts)
124 | if not self:is_recording() then
125 | return
126 | end
127 |
128 | table.insert(self.events, event_new(name, {
129 | attributes = opts.attributes,
130 | }))
131 | end
132 |
133 | ------------------------------------------------------------------
134 | -- update span name
135 | ------------------------------------------------------------------
136 | function _M.set_name(self, name)
137 | self.name = name
138 | end
139 |
140 | function _M.tracer_provider(self)
141 | return self.tracer.provider
142 | end
143 |
144 | function _M.plain(self)
145 | return {
146 | tracer = { il = self.tracer.il },
147 | parent_ctx = self.parent_ctx:plain(),
148 | ctx = self.ctx:plain(),
149 | name = self.name,
150 | start_time = self.start_time,
151 | end_time = self.end_time,
152 | kind = self.kind,
153 | attributes = self.attributes,
154 | events = self.events,
155 | }
156 | end
157 |
158 | return _M
159 |
--------------------------------------------------------------------------------
/lib/opentelemetry/context.lua:
--------------------------------------------------------------------------------
1 | local baggage = require("opentelemetry.baggage")
2 | local otel_global = require("opentelemetry.global")
3 | local non_recording_span_new = require("opentelemetry.trace.non_recording_span").new
4 | local noop_span = require("opentelemetry.trace.noop_span")
5 | local util = require("opentelemetry.util")
6 |
7 | local _M = {
8 | }
9 |
10 | local mt = {
11 | __index = _M
12 | }
13 |
14 | local context_key = "__opentelemetry_context__"
15 | local baggage_context_key = "__opentelemetry_baggage__"
16 |
17 | --------------------------------------------------------------------------------
18 | -- Create new context with set of entries
19 | --
20 | -- @return context
21 | --------------------------------------------------------------------------------
22 | function _M.new(entries, span)
23 | return setmetatable({ sp = span or noop_span.new(), entries = entries or {} }, mt)
24 | end
25 |
26 | --------------------------------------------------------------------------------
27 | -- Set this context as current by pushing it on stack stored at context_key.
28 | --
29 | -- @return token to be used for detaching
30 | --------------------------------------------------------------------------------
31 | function _M.attach(self)
32 | if otel_global.get_context_storage()[context_key] then
33 | table.insert(otel_global.get_context_storage()[context_key], self)
34 | else
35 | otel_global.get_context_storage()[context_key] = { self }
36 | end
37 |
38 | -- the length of the stack is token used to detach context
39 | return #otel_global.get_context_storage()[context_key]
40 | end
41 |
42 | --------------------------------------------------------------------------------
43 | -- Detach current context, setting current context to previous element in stack
44 | -- If token does not match length of elements in stack, returns false and error
45 | -- string.
46 | --
47 | -- @return boolean, string
48 | --------------------------------------------------------------------------------
49 | function _M.detach(self, token)
50 | if #otel_global.get_context_storage()[context_key] == token then
51 | table.remove(otel_global.get_context_storage()[context_key])
52 | return true, nil
53 | else
54 | local error_message = "Token does not match (" ..
55 | #otel_global.get_context_storage()[context_key] ..
56 | " context entries in stack, token provided was " .. token .. ")."
57 | ngx.log(ngx.WARN, error_message)
58 | return false, error_message
59 | end
60 | end
61 |
62 | --------------------------------------------------------------------------------
63 | -- Get current context, which is the final element in stack stored at
64 | -- context_key.
65 | --
66 | -- @return boolean, string
67 | --------------------------------------------------------------------------------
68 | function _M.current()
69 | if otel_global.get_context_storage()[context_key] then
70 | return otel_global.get_context_storage()[context_key][#otel_global.get_context_storage()[context_key]]
71 | else
72 | return _M.new()
73 | end
74 | end
75 |
76 | --------------------------------------------------------------------------------
77 | -- Retrieve value for key in context.
78 | --
79 | -- @key key for which to set the value in context
80 | -- @return value stored at key
81 | --------------------------------------------------------------------------------
82 | function _M.get(self, key)
83 | return self.entries[key]
84 | end
85 |
86 | --------------------------------------------------------------------------------
87 | -- Set value for key in context. This returns a new context object.
88 | --
89 | -- @key key for which to set the value in context
90 | -- @value value to set
91 | -- @return context
92 | --------------------------------------------------------------------------------
93 | function _M.set(self, key, value)
94 | local vals = util.shallow_copy_table(self.entries)
95 | vals[key] = value
96 | return self.new(vals, self.sp)
97 | end
98 |
99 | --------------------------------------------------------------------------------
100 | -- Inject baggage into current context
101 | --
102 | -- @baggage baggage instance to inject
103 | -- @return context
104 | --------------------------------------------------------------------------------
105 | function _M.inject_baggage(self, baggage)
106 | return self:set(baggage_context_key, baggage)
107 | end
108 |
109 | --------------------------------------------------------------------------------
110 | -- Extract baggage from context
111 | --
112 | -- @return baggage
113 | --------------------------------------------------------------------------------
114 | function _M.extract_baggage(self)
115 | return self:get(baggage_context_key) or baggage.new({})
116 | end
117 |
118 | function _M.with_span(self, span)
119 | return self.new(util.shallow_copy_table(self.entries or {}), span)
120 | end
121 |
122 | function _M.with_span_context(self, span_context)
123 | return self:with_span(non_recording_span_new(nil, span_context))
124 | end
125 |
126 | function _M.span_context(self)
127 | return self.sp:context()
128 | end
129 |
130 | function _M.span(self)
131 | return self.sp
132 | end
133 |
134 | return _M
135 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/tracestate.lua:
--------------------------------------------------------------------------------
1 | local _M = {
2 | MAX_KEY_LEN = 256,
3 | MAX_VAL_LEN = 256,
4 | MAX_ENTRIES = 32,
5 | }
6 |
7 | local mt = {
8 | __index = _M
9 | }
10 |
11 | local function validate_member_key(key)
12 | if #key > _M.MAX_KEY_LEN then
13 | return nil
14 | end
15 |
16 | local valid_key = string.match(key, [[^%s*([a-z][_0-9a-z%-*/]*)$]])
17 | if not valid_key then
18 | local tenant_id, system_id = string.match(key, [[^%s*([a-z0-9][_0-9a-z%-*/]*)@([a-z][_0-9a-z%-*/]*)$]])
19 | if not tenant_id or not system_id then
20 | return nil
21 | end
22 | if #tenant_id > 241 or #system_id > 14 then
23 | return nil
24 | end
25 | return tenant_id .. "@" .. system_id
26 | end
27 |
28 | return valid_key
29 | end
30 |
31 | local function validate_member_value(value)
32 | if #value > _M.MAX_VAL_LEN then
33 | return nil
34 | end
35 | return string.match(value,
36 | [[^([ !"#$%%&'()*+%-./0-9:;<>?@A-Z[\%]^_`a-z{|}~]*[!"#$%%&'()*+%-./0-9:;<>?@A-Z[\%]^_`a-z{|}~])%s*$]])
37 | end
38 |
39 | function _M.new(values)
40 | local self = { values = values }
41 | return setmetatable(self, mt)
42 | end
43 |
44 | --------------------------------------------------------------------------------
45 | -- Parse tracestate header into a tracestate
46 | --
47 | -- @return tracestate
48 | --------------------------------------------------------------------------------
49 | function _M.parse_tracestate(tracestate)
50 | if not tracestate then
51 | return _M.new({})
52 | end
53 | -- if there is only one tracestate header it comes in as a string, otherwise its a table
54 | if type(tracestate) == "string" then
55 | tracestate = { tracestate }
56 | end
57 |
58 | local new_tracestate = {}
59 | local members_count = 0
60 | local error_message = "failed to parse tracestate"
61 | for _, item in ipairs(tracestate) do
62 | for member in string.gmatch(item, "([^,]+)") do
63 | if member ~= "" then
64 | local start_pos, end_pos = string.find(member, "=", 1, true)
65 | if not start_pos or start_pos == 1 then
66 | ngx.log(ngx.WARN, error_message)
67 | return _M.new({})
68 | end
69 | local key = validate_member_key(string.sub(member, 1, start_pos - 1))
70 | if not key then
71 | ngx.log(ngx.WARN, error_message)
72 | return _M.new({})
73 | end
74 | local value = validate_member_value(string.sub(member, end_pos + 1))
75 | if not value then
76 | ngx.log(ngx.WARN, error_message)
77 | return _M.new({})
78 | end
79 | members_count = members_count + 1
80 | if members_count > _M.MAX_ENTRIES then
81 | ngx.log(ngx.WARN, error_message)
82 | return _M.new({})
83 | end
84 | table.insert(new_tracestate, {key, value})
85 | end
86 | end
87 | end
88 |
89 | return _M.new(new_tracestate)
90 | end
91 |
92 | --------------------------------------------------------------------------------
93 | -- Set the key value pair for the tracestate
94 | --
95 | -- @return tracestate
96 | --------------------------------------------------------------------------------
97 | function _M.set(self, key, value)
98 | if not validate_member_key(key) then
99 | return self
100 | end
101 | if not validate_member_value(value) then
102 | return self
103 | end
104 | self:del(key)
105 | if #self.values >= _M.MAX_ENTRIES then
106 | table.remove(self.values)
107 | ngx.log(ngx.WARN, "tracestate max values exceeded, removing rightmost entry")
108 | end
109 | table.insert(self.values, 1, {key, value})
110 | return self
111 | end
112 |
113 | --------------------------------------------------------------------------------
114 | -- Get the value for the current key from the tracestate
115 | --
116 | -- @return value
117 | --------------------------------------------------------------------------------
118 | function _M.get(self, key)
119 | for _, item in ipairs(self.values) do
120 | local ckey = item[1]
121 | if ckey == key then
122 | return item[2]
123 | end
124 | end
125 | return ""
126 | end
127 |
128 | --------------------------------------------------------------------------------
129 | -- Delete the key from the tracestate
130 | --
131 | -- @return tracestate
132 | --------------------------------------------------------------------------------
133 | function _M.del(self, key)
134 | local index = 0
135 | for i, item in ipairs(self.values) do
136 | local ckey = item[1]
137 | if ckey == key then
138 | index = i
139 | break
140 | end
141 | end
142 | if index ~= 0 then
143 | table.remove(self.values, index)
144 | end
145 | return self
146 | end
147 |
148 | --------------------------------------------------------------------------------
149 | -- Return the header value of the tracestate
150 | --
151 | -- @return string
152 | --------------------------------------------------------------------------------
153 | function _M.as_string(self)
154 | local output = {}
155 | for _, item in ipairs(self.values) do
156 | table.insert(output, item[1] .. "=" .. item[2])
157 | end
158 | return table.concat(output, ",")
159 | end
160 |
161 | return _M
162 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-4.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-4"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.4",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.api.trace.span_status"] = "lib/opentelemetry/api/trace/span_status.lua",
23 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
24 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
29 | ["opentelemetry.semantic_conventions.trace.aws"] = "lib/opentelemetry/semantic_conventions/trace/aws.lua",
30 | ["opentelemetry.semantic_conventions.trace.cloudevents"] = "lib/opentelemetry/semantic_conventions/trace/cloudevents.lua",
31 | ["opentelemetry.semantic_conventions.trace.compatibility"] = "lib/opentelemetry/semantic_conventions/trace/compatibility.lua",
32 | ["opentelemetry.semantic_conventions.trace.database"] = "lib/opentelemetry/semantic_conventions/trace/database.lua",
33 | ["opentelemetry.semantic_conventions.trace.exporter"] = "lib/opentelemetry/semantic_conventions/trace/exporter.lua",
34 | ["opentelemetry.semantic_conventions.trace.faas"] = "lib/opentelemetry/semantic_conventions/trace/faas.lua",
35 | ["opentelemetry.semantic_conventions.trace.feature"] = "lib/opentelemetry/semantic_conventions/trace/feature.lua",
36 | ["opentelemetry.semantic_conventions.trace.general"] = "lib/opentelemetry/semantic_conventions/trace/general.lua",
37 | ["opentelemetry.semantic_conventions.trace.http"] = "lib/opentelemetry/semantic_conventions/trace/http.lua",
38 | ["opentelemetry.semantic_conventions.trace.messaging"] = "lib/opentelemetry/semantic_conventions/trace/messaging.lua",
39 | ["opentelemetry.semantic_conventions.trace.rpc"] = "lib/opentelemetry/semantic_conventions/trace/rpc.lua",
40 | ["opentelemetry.semantic_conventions.trace.trace"] = "lib/opentelemetry/semantic_conventions/trace/trace.lua",
41 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
42 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
43 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
44 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
45 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
46 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
47 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
48 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
49 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
50 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
51 | ["opentelemetry.trace.simple_span_processor"] = "lib/opentelemetry/trace/simple_span_processor.lua",
52 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
53 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
54 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
55 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
56 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
57 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
58 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
59 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
60 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
61 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
62 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
63 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
64 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
65 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
66 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
67 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
68 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
69 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
70 | ["opentelemetry.trace.tracestate"] = "lib/opentelemetry/trace/tracestate.lua",
71 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
72 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
73 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-5.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-5"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.5",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.api.trace.span_status"] = "lib/opentelemetry/api/trace/span_status.lua",
23 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
24 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
29 | ["opentelemetry.semantic_conventions.trace.aws"] = "lib/opentelemetry/semantic_conventions/trace/aws.lua",
30 | ["opentelemetry.semantic_conventions.trace.cloudevents"] = "lib/opentelemetry/semantic_conventions/trace/cloudevents.lua",
31 | ["opentelemetry.semantic_conventions.trace.compatibility"] = "lib/opentelemetry/semantic_conventions/trace/compatibility.lua",
32 | ["opentelemetry.semantic_conventions.trace.database"] = "lib/opentelemetry/semantic_conventions/trace/database.lua",
33 | ["opentelemetry.semantic_conventions.trace.exporter"] = "lib/opentelemetry/semantic_conventions/trace/exporter.lua",
34 | ["opentelemetry.semantic_conventions.trace.faas"] = "lib/opentelemetry/semantic_conventions/trace/faas.lua",
35 | ["opentelemetry.semantic_conventions.trace.feature"] = "lib/opentelemetry/semantic_conventions/trace/feature.lua",
36 | ["opentelemetry.semantic_conventions.trace.general"] = "lib/opentelemetry/semantic_conventions/trace/general.lua",
37 | ["opentelemetry.semantic_conventions.trace.http"] = "lib/opentelemetry/semantic_conventions/trace/http.lua",
38 | ["opentelemetry.semantic_conventions.trace.messaging"] = "lib/opentelemetry/semantic_conventions/trace/messaging.lua",
39 | ["opentelemetry.semantic_conventions.trace.rpc"] = "lib/opentelemetry/semantic_conventions/trace/rpc.lua",
40 | ["opentelemetry.semantic_conventions.trace.trace"] = "lib/opentelemetry/semantic_conventions/trace/trace.lua",
41 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
42 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
43 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
44 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
45 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
46 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
47 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
48 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
49 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
50 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
51 | ["opentelemetry.trace.simple_span_processor"] = "lib/opentelemetry/trace/simple_span_processor.lua",
52 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
53 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
54 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
55 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
56 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
57 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
58 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
59 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
60 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
61 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
62 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
63 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
64 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
65 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
66 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
67 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
68 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
69 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
70 | ["opentelemetry.trace.tracestate"] = "lib/opentelemetry/trace/tracestate.lua",
71 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
72 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
73 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/rockspec/opentelemetry-lua-0.2-6.rockspec:
--------------------------------------------------------------------------------
1 | package = "opentelemetry-lua"
2 | version = "0.2-6"
3 | source = {
4 | url = "git://github.com/yangxikun/opentelemetry-lua",
5 | branch = "v0.2.6",
6 | }
7 |
8 | description = {
9 | summary = "The OpenTelemetry Lua SDK. This is repo's default rockspec, which is used to test the HEAD commit.",
10 | homepage = "https://github.com/yangxikun/opentelemetry-lua",
11 | license = "Apache License 2.0"
12 | }
13 |
14 | dependencies = {
15 | "lua-protobuf = 0.3.3",
16 | "lua-resty-http = 0.16.1-0",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["opentelemetry.api.trace.span_status"] = "lib/opentelemetry/api/trace/span_status.lua",
23 | ["opentelemetry.global"] = "lib/opentelemetry/global.lua",
24 | ["opentelemetry.context"] = "lib/opentelemetry/context.lua",
25 | ["opentelemetry.attribute"] = "lib/opentelemetry/attribute.lua",
26 | ["opentelemetry.instrumentation_library"] = "lib/opentelemetry/instrumentation_library.lua",
27 | ["opentelemetry.resource"] = "lib/opentelemetry/resource.lua",
28 | ["opentelemetry.metrics_reporter"] = "lib/opentelemetry/metrics_reporter.lua",
29 | ["opentelemetry.semantic_conventions.trace.aws"] = "lib/opentelemetry/semantic_conventions/trace/aws.lua",
30 | ["opentelemetry.semantic_conventions.trace.cloudevents"] = "lib/opentelemetry/semantic_conventions/trace/cloudevents.lua",
31 | ["opentelemetry.semantic_conventions.trace.compatibility"] = "lib/opentelemetry/semantic_conventions/trace/compatibility.lua",
32 | ["opentelemetry.semantic_conventions.trace.database"] = "lib/opentelemetry/semantic_conventions/trace/database.lua",
33 | ["opentelemetry.semantic_conventions.trace.exporter"] = "lib/opentelemetry/semantic_conventions/trace/exporter.lua",
34 | ["opentelemetry.semantic_conventions.trace.faas"] = "lib/opentelemetry/semantic_conventions/trace/faas.lua",
35 | ["opentelemetry.semantic_conventions.trace.feature"] = "lib/opentelemetry/semantic_conventions/trace/feature.lua",
36 | ["opentelemetry.semantic_conventions.trace.general"] = "lib/opentelemetry/semantic_conventions/trace/general.lua",
37 | ["opentelemetry.semantic_conventions.trace.http"] = "lib/opentelemetry/semantic_conventions/trace/http.lua",
38 | ["opentelemetry.semantic_conventions.trace.messaging"] = "lib/opentelemetry/semantic_conventions/trace/messaging.lua",
39 | ["opentelemetry.semantic_conventions.trace.rpc"] = "lib/opentelemetry/semantic_conventions/trace/rpc.lua",
40 | ["opentelemetry.semantic_conventions.trace.trace"] = "lib/opentelemetry/semantic_conventions/trace/trace.lua",
41 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
42 | ["opentelemetry.trace.event"] = "lib/opentelemetry/trace/event.lua",
43 | ["opentelemetry.trace.exporter.http_client"] = "lib/opentelemetry/trace/exporter/http_client.lua",
44 | ["opentelemetry.trace.exporter.circuit"] = "lib/opentelemetry/trace/exporter/circuit.lua",
45 | ["opentelemetry.trace.exporter.console"] = "lib/opentelemetry/trace/exporter/console.lua",
46 | ["opentelemetry.trace.exporter.encoder"] = "lib/opentelemetry/trace/exporter/encoder.lua",
47 | ["opentelemetry.trace.exporter.otlp"] = "lib/opentelemetry/trace/exporter/otlp.lua",
48 | ["opentelemetry.trace.exporter.pb"] = "lib/opentelemetry/trace/exporter/pb.lua",
49 | ["opentelemetry.trace.id_generator"] = "lib/opentelemetry/trace/id_generator.lua",
50 | ["opentelemetry.trace.batch_span_processor"] = "lib/opentelemetry/trace/batch_span_processor.lua",
51 | ["opentelemetry.trace.simple_span_processor"] = "lib/opentelemetry/trace/simple_span_processor.lua",
52 | ["opentelemetry.trace.non_recording_span"] = "lib/opentelemetry/trace/non_recording_span.lua",
53 | ["opentelemetry.trace.noop_span"] = "lib/opentelemetry/trace/noop_span.lua",
54 | ["opentelemetry.trace.propagation.text_map.trace_context_propagator"] = "lib/opentelemetry/trace/propagation/text_map/trace_context_propagator.lua",
55 | ["opentelemetry.trace.propagation.text_map.composite_propagator"] = "lib/opentelemetry/trace/propagation/text_map/composite_propagator.lua",
56 | ["opentelemetry.trace.propagation.text_map.getter"] = "lib/opentelemetry/trace/propagation/text_map/getter.lua",
57 | ["opentelemetry.trace.propagation.text_map.setter"] = "lib/opentelemetry/trace/propagation/text_map/setter.lua",
58 | ["opentelemetry.trace.propagation.text_map.noop_propagator"] = "lib/opentelemetry/trace/propagation/text_map/noop_propagator.lua",
59 | ["opentelemetry.trace.recording_span"] = "lib/opentelemetry/trace/recording_span.lua",
60 | ["opentelemetry.trace.sampling.always_off_sampler"] = "lib/opentelemetry/trace/sampling/always_off_sampler.lua",
61 | ["opentelemetry.trace.sampling.always_on_sampler"] = "lib/opentelemetry/trace/sampling/always_on_sampler.lua",
62 | ["opentelemetry.trace.sampling.parent_base_sampler"] = "lib/opentelemetry/trace/sampling/parent_base_sampler.lua",
63 | ["opentelemetry.trace.sampling.result"] = "lib/opentelemetry/trace/sampling/result.lua",
64 | ["opentelemetry.trace.sampling.trace_id_ratio_sampler"] = "lib/opentelemetry/trace/sampling/trace_id_ratio_sampler.lua",
65 | ["opentelemetry.trace.span_context"] = "lib/opentelemetry/trace/span_context.lua",
66 | ["opentelemetry.trace.span_kind"] = "lib/opentelemetry/trace/span_kind.lua",
67 | ["opentelemetry.trace.span_status"] = "lib/opentelemetry/trace/span_status.lua",
68 | ["opentelemetry.trace.tracer"] = "lib/opentelemetry/trace/tracer.lua",
69 | ["opentelemetry.trace.tracer_provider"] = "lib/opentelemetry/trace/tracer_provider.lua",
70 | ["opentelemetry.trace.tracestate"] = "lib/opentelemetry/trace/tracestate.lua",
71 | ["opentelemetry.baggage"] = "lib/opentelemetry/baggage.lua",
72 | ["opentelemetry.baggage.propagation.text_map.baggage_propagator"] = "lib/opentelemetry/baggage/propagation/text_map/baggage_propagator.lua",
73 | ["opentelemetry.util"] = "lib/opentelemetry/util.lua"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/opentelemetry/trace/exporter/otlp.lua:
--------------------------------------------------------------------------------
1 | local encoder = require("opentelemetry.trace.exporter.encoder")
2 | local pb = require("opentelemetry.trace.exporter.pb")
3 | local otel_global = require("opentelemetry.global")
4 | local util = require("opentelemetry.util")
5 | local BACKOFF_RETRY_LIMIT = 3
6 | local DEFAULT_TIMEOUT_MS = 10000
7 | local exporter_request_duration_metric = "otel.otlp_exporter.request_duration"
8 | local circuit = require("opentelemetry.trace.exporter.circuit")
9 |
10 | local _M = {
11 | }
12 |
13 | local mt = {
14 | __index = _M
15 | }
16 |
17 | function _M.new(http_client, timeout_ms, circuit_reset_timeout_ms, circuit_open_threshold)
18 | local self = {
19 | client = http_client,
20 | timeout_ms = timeout_ms or DEFAULT_TIMEOUT_MS,
21 | circuit = circuit.new({
22 | reset_timeout_ms = circuit_reset_timeout_ms,
23 | failure_threshold = circuit_open_threshold
24 | })
25 | }
26 | return setmetatable(self, mt)
27 | end
28 |
29 | --------------------------------------------------------------------------------
30 | -- Repeatedly make calls to collector until success, failure threshold or
31 | -- timeout
32 | --
33 | -- @param exporter The exporter to use for the collector call
34 | -- @param pb_encoded_body protobuf-encoded body to send to the collector
35 | -- @return true if call succeeded; false if call failed
36 | -- @return nil if call succeeded; error message string if call failed
37 | --------------------------------------------------------------------------------
38 | local function call_collector(exporter, pb_encoded_body)
39 | local start_time_ms = util.gettimeofday_ms()
40 | local failures = 0
41 | local res
42 | local res_error
43 |
44 | while failures < BACKOFF_RETRY_LIMIT do
45 | local current_time = util.gettimeofday_ms()
46 | if current_time - start_time_ms > exporter.timeout_ms then
47 | local err_message = "Collector retries timed out (timeout " .. exporter.timeout_ms .. ")"
48 | ngx.log(ngx.WARN, err_message)
49 | return false, err_message
50 | end
51 |
52 | if not exporter.circuit:should_make_request() then
53 | ngx.log(ngx.INFO, "Circuit breaker is open")
54 | return false, "Circuit breaker is open"
55 | end
56 |
57 | -- Make request
58 | res, res_error = exporter.client:do_request(pb_encoded_body)
59 | local after_time = util.gettimeofday_ms()
60 | otel_global.metrics_reporter:record_value(
61 | exporter_request_duration_metric, after_time - current_time)
62 |
63 | if not res then
64 | exporter.circuit:record_failure()
65 | failures = failures + 1
66 | ngx.sleep(util.random_float(2 ^ failures))
67 | ngx.log(ngx.INFO, "Retrying call to collector (retry #" .. failures .. ")")
68 | else
69 | exporter.circuit:record_success()
70 | return true, nil
71 | end
72 | end
73 |
74 | return false, res_error or "unknown"
75 | end
76 |
77 | function _M.encode_spans(self, spans)
78 | assert(spans[1])
79 |
80 | local body = {
81 | resource_spans = {
82 | {
83 | resource = {
84 | attributes = spans[1].tracer.provider.resource.attrs,
85 | dropped_attributes_count = 0,
86 | },
87 | instrumentation_library_spans = {
88 | {
89 | instrumentation_library = {
90 | name = spans[1].tracer.il.name,
91 | version = spans[1].tracer.il.version,
92 | },
93 | spans = {}
94 | },
95 | },
96 | }
97 | }
98 | }
99 | local tracers = {}
100 | local providers = {}
101 | tracers[spans[1].tracer] = 1
102 | providers[spans[1].tracer.provider] = 1
103 | for _, span in ipairs(spans) do
104 | local rs_idx = providers[span.tracer.provider]
105 | local ils_idx = tracers[span.tracer]
106 | if not rs_idx then
107 | rs_idx = #body.resource_spans + 1
108 | ils_idx = 1
109 | providers[span.tracer.provider] = rs_idx
110 | tracers[span.tracer] = ils_idx
111 | table.insert(
112 | body.resource_spans,
113 | {
114 | resource = {
115 | attributes = span.tracer.provider.resource.attrs,
116 | dropped_attributes_count = 0,
117 | },
118 | instrumentation_library_spans = {
119 | {
120 | instrumentation_library = {
121 | name = span.tracer.il.name,
122 | version = span.tracer.il.version,
123 | },
124 | spans = {}
125 | },
126 | },
127 | })
128 | elseif not ils_idx then
129 | ils_idx = #body.resource_spans[rs_idx].instrumentation_library_spans + 1
130 | tracers[span.tracer] = ils_idx
131 | table.insert(
132 | body.resource_spans[rs_idx].instrumentation_library_spans,
133 | {
134 | instrumentation_library = {
135 | name = span.tracer.il.name,
136 | version = span.tracer.il.version,
137 | },
138 | spans = {}
139 | })
140 | end
141 | table.insert(
142 | body.resource_spans[rs_idx].instrumentation_library_spans[ils_idx].spans,
143 | encoder.for_otlp(span))
144 | end
145 | return body
146 | end
147 |
148 | function _M.export_spans(self, spans)
149 | return call_collector(self, pb.encode(self:encode_spans(spans)))
150 | end
151 |
152 | function _M.shutdown(self)
153 |
154 | end
155 |
156 | return _M
157 |
--------------------------------------------------------------------------------