OTEL (OpenTelemetry) CLI

otel-cli, an application written in Node.js, is a command-line utility designed to
send OpenTelemetry traces to an external OpenTelemetry collector OTLP endpoint.
Its main use case is within shell scripts and other situations
where trace sending is most efficiently achieved by running an additional program.
Prerequisites
Setup
npm install -g otel-cli
After install, check whether it is installed successfully:
otel-cli --version
By this command, you should see the installed version number if everything is installed properly.
Configuration
Common
- --version - -V | | NO | | Output the CLI version number |
- --help - -h | | NO | | Display help for commands |
Commands
otel-cli export [options]: Create the span by given options and exports the created span to the OTEL collector OTLP endpoint.
otel-cli generate-id [options]: Generate id of the specified type (trace or span) and outputs the generated id.
otel-cli start-server [options]: Starts OTEL CLI server to be able to export traces in background.
otel-cli shutdown-server [options]: Gracefully shutdowns OTEL CLI server by exporting buffered traces before terminate.
otel-cli help [command]: Display help for the given command.
export command
- --verbose - -v | OTEL_CLI_VERBOSE=true | NO | | false | Enable verbose mode | --verbose |
- --endpoint <url> - -e <url> | OTEL_EXPORTER_OTLP_ENDPOINT=<url> | YES | | | OTEL Exporter OTLP endpoint | --endpoint https://collector.otel.io |
- --protocol <url> - -p <url> | OTEL_EXPORTER_OTLP_PROTOCOL=<protocol> | NO | - http/json - grpc | http/json | OTEL Exporter OTLP protocol | - --protocol http/json - --protocol grpc |
- --headers <key1=value1> <key2=value2> ... - -h <key1=value1> <key2=value2> ... | OTEL_EXPORTER_OTLP_HEADERS=key1=value1>,<key2=value2> | NO | | | OTEL Exporter OTLP headers - In CLI options, headers are specified as space ( ) seperated key-value pairs (key1=value1 key2=value2 key3=value3) - In environment variable, headers are specified as comma (,) seperated key-value pairs (key1=value1,key2=value2,key3=value3)
| --headers x-api-key=abcd-1234 x-project-id=efgh-5678 |
- --traceparent <header> - -tp <header> | TRACEPARENT=<header> | NO | | | Traceparent header in W3C trace context format | --traceparent 00-84b54e9330faae5350f0dd8673c98146-279fa73bc935cc05-01 |
- --traceparent-disable - -tpd | OTEL_CLI_TRACEPARENT_DISABLE=true | NO | | false | Disable traceparent header based W3C trace context propagation for the exported span | --traceparent-disable |
- --traceparent-print - -tpp | OTEL_CLI_TRACEPARENT_PRINT=true | NO | | false | Print traceparent header in W3C trace context format for the exported span (the exported span id will be injected as parent span id in the header) | --traceparent-print |
- --trace-id <id> - -t <id> | OTEL_CLI_TRACE_ID=<id> | NO | | | Trace id | --trace-id 84b54e9330faae5350f0dd8673c98146 |
- --span-id <id> - -s <id> | | NO | | | Span id | --span-id b2746bb26cd13726 |
- --parent-span-id <id> - -p <id> | | NO | | | Parent span id | --parent-span-id 279fa73bc935cc05 |
- --name <name> - -s <name> | | YES | | | Span name | --name doPayment |
- --service-name <name> - -sn <name> | - OTEL_CLI_SERVICE_NAME=<service-name> - OTEL_SERVICE_NAME=<service-name> | YES | | | Service name | --service-name payment-service |
- --kind <kind> - -k <kind> | | NO | - INTERNAL - SERVER - CLIENT - PRODUCER - CONSUMER
| INTERNAL | Span kind | - --kind CLIENT - --kind PRODUCER - ...
|
- --start-time-nanos <nanos> | | NO | | | Start time in nanoseconds | --start-time-nanos 1688811191123456789 |
- --start-time-micros <micros> | | NO | | | Start time in microseconds | --start-time-micros 1688811191123456 |
- --start-time-millis <millis> | | NO | | | Start time in milliseconds | --start-time-millis 1688811191123 |
- --start-time-secs <secs> | | NO | | | Start time in seconds | --start-time-secs 1688811191 |
- --end-time-nanos <nanos> | | NO | | | End time in nanoseconds | --end-time-nanos 1688811192123456789 |
- --end-time-micros <micros> | | NO | | | End time in microseconds | --end-time-micros 1688811192123456 |
- --end-time-millis <millis> | | NO | | | End time in milliseconds | --end-time-millis 1688811192123 |
- --end-time-secs <secs> | | NO | | | End time in seconds | --start-time-secs 1688811192 |
- --status-code <code> - -sc <code> | | NO | - UNSET - OK - ERROR
| UNSET | Status code | - --status-code OK - --status-code ERROR - ...
|
- --status-message <message> - -sm <message> | | NO | | | Status message | --status-message "Invalid argument" |
- --attributes <key-value-pairs...> - -a <key-value-pairs...> | | NO | | | Span attributes as space ( ) seperated key-value pairs (key1=value1 key2=value2 key3=value3) | --attributes key1=value1 key2=\"my value\" key3=true key4=123 key5=67.89 key6=\"456\" |
- --server-port - -sp <port> | OTEL_CLI_SERVER_PORT=<port> | NO | | 7777 | OTEL CLI server port for communicating over to export traces asynchronously in background | - --server-port 12345 - -sp 12345 |
How OTEL Exporter OTLP HTTP traces endpoint resolved?
- if
--endpoint (or -e) option is specified,
OTLP HTTP traces endpoint is generated from the option value by appending /v1/traces to the end of the value.
- Else, if
OTEL_EXPORTER_OTLP_ENDPOINT environment variable is specified,
OTLP HTTP traces endpoint is generated from the environment variable value by appending /v1/traces to the end of the value.
- Else, CLI fails with the error.
How trace id is resolved?
- If
--trace-id (or -t) option is specified,
trace id is used from the option value.
- Else, if
OTEL_CLI_TRACE_ID environment variable is specified,
trace id is used from the environment variable value.
- Else, if
--traceparent option (or -tp) is specified,
trace id is extracted from the traceparent header option value.
- Else, if
TRACEPARENT environment variable is specified,
trace id is extracted from the traceparent header environment variable value.
- Else, CLI fails with the error (
Trace id is not specified).
How span id is resolved?
- If
--span-id (or -s) option is specified,
span id is used from the option value.
- Else, random span id (16-hex-character lowercase string) is generated.
How parent span id resolved?
- If
--parent-span-id (or -p) option is specified,
parent span id is used from the option value.
- Else, if
OTEL_CLI_PARENT_SPAN_ID environment variable is specified,
parent span id is used from the environment variable value.
- Else, if
--traceparent option (or -tp) is specified,
parent span id is extracted from the traceparent header option value.
- Else, if
TRACEPARENT environment variable is specified,
parent span id is extracted from the traceparent header environment variable value.
- Else, it is assumed that there is no associated parent span.
How start time is resolved?
- If
--start-time-nanos option is specified,
start time is used from the option value.
- Else, if
--start-time-micros option is specified,
start time is calculated by multiplying the option value by 1000 (to convert microseconds to nanoseconds).
- Else, if
--start-time-millis option is specified,
start time is calculated by multiplying the option value by 1000000 (to convert milliseconds to nanoseconds).
- Else, if
--start-time-secs option is specified,
start time is calculated by multiplying the option value by 1000000000 (to convert seconds to nanoseconds).
- Else, CLI fails with the error (
Span start time must be specified in one of the supported formats (nanoseconds, microseconds, milliseconds, or seconds)!).
How end time is resolved?
- If
--end-time-nanos option is specified,
end time is used from the option value.
- Else, if
--end-time-micros option is specified,
end time is calculated by multiplying the option value by 1000 (to convert microseconds to nanoseconds).
- Else, if
--end-time-millis option is specified,
end time is calculated by multiplying the option value by 1000000 (to convert milliseconds to nanoseconds).
- Else, if
--end-time-secs option is specified,
end time is calculated by multiplying the option value by 1000000000 (to convert seconds to nanoseconds).
- Else, CLI fails with the error (
Span end time must be specified in one of the supported formats (nanoseconds, microseconds, milliseconds, or seconds)!).
How to export traces asynchronously in background?
By default, export command sends traces synchronously to the configured OTLP endpoint by blocking the caller in the script.
But OTEL CLI also supports sending traces asynchronously through OTEL CLI server by exporting traces to the OTEL CLI server first over the specified HTTP port.
Then OTEL CLI server buffers the received traces and sends them to the target OTLP endpoint asynchronously in background.
Start OTEL CLI server
To be able to start OTEL CLI server, you can use start-server command.
By default, start-server command is blocking, so you should run it in the background yourself to not to block your program/script.
For example, in the Linux and MacOS environments, you can use & operation after the command to run it in the background:
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH-HEADER-NAME>=<YOUR-OTEL-VENDOR-API-AUTH-TOKEN>
export OTEL_CLI_SERVER_PORT=12345
otel-cli start-server &
or by specifying configurations through the options:
otel-cli start-server \
--endpoint <YOUR-OTEL-VENDOR-OTLP-ENDPOINT> \
--headers <YOUR-OTEL-VENDOR-API-AUTH-HEADER-NAME>=<YOUR-OTEL-VENDOR-API-AUTH-TOKEN> \
--server-port 12345 \
&
Shutdown OTEL CLI server
Since the OTEL CLI server buffers the received traces to be send them asynchronously,
it should be shutdown gracefully to flush the buffered traces by exporting them to the configured OTLP endpoint before terminated.
Otherwise, some of the traces might be lost.
To be able to shutdown OTEL CLI server gracefully, you can use shutdown-server command
by specifying the same port number you use while starting server.
export OTEL_CLI_SERVER_PORT=12345
otel-cli shutdown-server
or by specifying configurations through the options:
otel-cli shutdown-server --server-port 12345
:warning:
Even you don't shutdown the server manually by yourself,
OTEL CLI server shutdown itself automatically when the parent process (program or script) exits.
But in any way, it is good practice to shutdown by yourself explicitly.
generate-id command
- --verbose - -v | OTEL_CLI_VERBOSE=true | NO | | false | Enables verbose mode | --verbose |
- --type <id-type> - -t <id-type> | | YES | - trace - span | | Type of the id to be generated | - --type trace - --type span |
start-server command
- --verbose - -v | OTEL_CLI_VERBOSE=true | NO | | false | Enable verbose mode | --verbose |
- --endpoint <url> - -e <url> | OTEL_EXPORTER_OTLP_ENDPOINT=<url> | YES | | | OTEL Exporter OTLP endpoint | --endpoint https://collector.otel.io |
- --protocol <url> - -p <url> | OTEL_EXPORTER_OTLP_PROTOCOL=<protocol> | NO | - http/json - grpc | http/json | OTEL Exporter OTLP protocol | - --protocol http/json - --protocol grpc |
- --headers <key1=value1> <key2=value2> ... - -h <key1=value1> <key2=value2> ... | OTEL_EXPORTER_OTLP_HEADERS=key1=value1>,<key2=value2> | NO | | | OTEL Exporter OTLP headers - In CLI options, headers are specified as space ( ) seperated key-value pairs (key1=value1 key2=value2 key3=value3) - In environment variable, headers are specified as comma (,) seperated key-value pairs (key1=value1,key2=value2,key3=value3)
| --headers x-api-key=abcd-1234 x-project-id=efgh-5678 |
- --server-port - -sp <port> | OTEL_CLI_SERVER_PORT=<port> | NO | | 7777 | OTEL CLI server port to start on | - --server-port 12345 - -sp 12345 |
shutdown-server command
- --verbose - -v | OTEL_CLI_VERBOSE=true | NO | | false | Enable verbose mode | --verbose |
- --server-port - -sp <port> | OTEL_CLI_SERVER_PORT=<port> | NO | | 7777 | OTEL CLI server port for communicating over to shutdown gracefully | - --server-port 12345 - -sp 12345 |
Examples
Export trace [Linux]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH-HEADER-NAME>=<YOUR-OTEL-VENDOR-API-AUTH-TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
start_time=$(date +%s%9N)
pushd auth-service
mvn clean package
popd
end_time=$(date +%s%9N)
otel-cli export \
--name build-auth-service --start-time-nanos ${start_time} --end-time-nanos ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time=$(date +%s%9N)
pushd payment-service
npm run build
popd
end_time=$(date +%s%9N)
otel-cli export \
--name build-payment-service --start-time-nanos ${start_time} --end-time-nanos ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
Export trace [MacOS]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH_HEADER_NAME>=<YOUR-OTEL-VENDOR-API-AUTH_TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
start_time=$(node -e 'console.log(Date.now())')
pushd auth-service
mvn clean package
popd
end_time=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-auth-service --start-time-millis ${start_time} --end-time-millis ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time=$(node -e 'console.log(Date.now())')
pushd payment-service
npm run build
popd
end_time=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-payment-service --start-time-millis ${start_time} --end-time-millis ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
Export trace (Parent-Child) [Linux]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH_HEADER_NAME>=<YOUR-OTEL-VENDOR-API-AUTH_TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
root_span_id=$(otel-cli generate-id -t span)
start_time0=$(date +%s%9N)
start_time1=$(date +%s%9N)
pushd auth-service
mvn clean package
popd
end_time1=$(date +%s%9N)
otel-cli export \
--name build-auth-service --parent-span-id ${root_span_id} --start-time-nanos ${start_time1} --end-time-nanos ${end_time1} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time2=$(date +%s%9N)
pushd payment-service
npm run build
popd
end_time2=$(date +%s%9N)
otel-cli export \
--name build-payment-service --parent-span-id ${root_span_id} --start-time-millis ${start_time2} --end-time-millis ${end_time2} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
end_time0=$(date +%s%9N)
otel-cli export \
--name build-services --span-id ${root_span_id} --start-time-millis ${start_time0} --end-time-millis ${end_time0} \
--kind INTERNAL --status-code OK
Export trace (Parent-Child) [MacOS]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH_HEADER_NAME>=<YOUR-OTEL-VENDOR-API-AUTH_TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
root_span_id=$(otel-cli generate-id -t span)
start_time0=$(node -e 'console.log(Date.now())')
start_time1=$(node -e 'console.log(Date.now())')
pushd auth-service
mvn clean package
popd
end_time1=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-auth-service --parent-span-id ${root_span_id} --start-time-millis ${start_time1} --end-time-millis ${end_time1} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time2=$(node -e 'console.log(Date.now())')
pushd payment-service
npm run build
popd
end_time2=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-payment-service --parent-span-id ${root_span_id} --start-time-millis ${start_time2} --end-time-millis ${end_time2} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
end_time0=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-services --span-id ${root_span_id} --start-time-millis ${start_time0} --end-time-millis ${end_time0} \
--kind INTERNAL --status-code OK
Export trace asynchronously in background [Linux]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH-HEADER-NAME>=<YOUR-OTEL-VENDOR-API-AUTH-TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_SERVER_PORT=12345
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
otel-cli start-server &
function shutdown_server {
otel-cli shutdown-server
}
trap shutdown_server EXIT
start_time=$(date +%s%9N)
pushd auth-service
mvn clean package
popd
end_time=$(date +%s%9N)
otel-cli export \
--name build-auth-service --start-time-nanos ${start_time} --end-time-nanos ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time=$(date +%s%9N)
pushd payment-service
npm run build
popd
end_time=$(date +%s%9N)
otel-cli export \
--name build-payment-service --start-time-nanos ${start_time} --end-time-nanos ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
Export trace asynchronously in background [MacOS]
export OTEL_EXPORTER_OTLP_ENDPOINT=<YOUR-OTEL-VENDOR-OTLP-ENDPOINT>
export OTEL_EXPORTER_OTLP_HEADERS=<YOUR-OTEL-VENDOR-API-AUTH-HEADER-NAME>=<YOUR-OTEL-VENDOR-API-AUTH-TOKEN>
export OTEL_SERVICE_NAME=build
export OTEL_CLI_SERVER_PORT=12345
export OTEL_CLI_TRACE_ID=$(otel-cli generate-id -t trace)
otel-cli start-server &
function shutdown_server {
otel-cli shutdown-server
}
trap shutdown_server EXIT
start_time=$(node -e 'console.log(Date.now())')
pushd auth-service
mvn clean package
popd
end_time=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-auth-service --start-time-millis ${start_time} --end-time-millis ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=auth-service buildTool=maven runtime=java
start_time=$(node -e 'console.log(Date.now())')
pushd payment-service
npm run build
popd
end_time=$(node -e 'console.log(Date.now())')
otel-cli export \
--name build-payment-service --start-time-millis ${start_time} --end-time-millis ${end_time} \
--kind INTERNAL --status-code OK --attributes serviceName=payment-service buildTool=npm runtime=node
Roadmap
- Automated bash command tracing by wrapping command to be executed
http/protobuf support as OTLP protocol
grpc support as OTLP protocol
- Batch transmission support while sending traces to OTLP endpoint to reduce network RTT (Round Trip Time)
Issues and Feedback

Please use GitHub Issues for any bug report, feature request and support.
Contribution

If you would like to contribute, please
- Fork the repository on GitHub and clone your fork.
- Create a branch for your changes and make your changes on it.
- Send a pull request by explaining clearly what is your contribution.
Tip:
Please check the existing pull requests for similar contributions and
consider submit an issue to discuss the proposed feature before writing code.
License
Licensed under Apache License 2.0.