Skip to content

Instantly share code, notes, and snippets.

@uzimith

uzimith/main.ts Secret

Created March 9, 2026 10:42
Show Gist options
  • Select an option

  • Save uzimith/7d0048852e717ef7d993aeffc238d18f to your computer and use it in GitHub Desktop.

Select an option

Save uzimith/7d0048852e717ef7d993aeffc238d18f to your computer and use it in GitHub Desktop.
otel-hello
import { resourceFromAttributes } from "@opentelemetry/resources";
import {
ATTR_SERVICE_NAME,
ATTR_SERVICE_VERSION,
} from "@opentelemetry/semantic-conventions";
import { NodeSDK } from "@opentelemetry/sdk-node";
import { trace, SpanStatusCode } from "@opentelemetry/api";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
interface ParsedArgs {
otelEndpoint: string | null;
names: string[];
}
function parseArgs(argv: string[]): ParsedArgs {
const args = argv.slice(2);
let otelEndpoint: string | null = null;
const names: string[] = [];
for (let i = 0; i < args.length; i++) {
if (args[i] === "--otel") {
otelEndpoint = args[++i];
if (!otelEndpoint) {
console.error("Error: --otel requires an endpoint URL");
process.exit(1);
}
} else if (args[i] === "--help" || args[i] === "-h") {
console.log(
`Usage: npx tsx src/main.ts [--otel <endpoint>] [name...]
--otel <endpoint> OTLP HTTP endpoint (e.g. http://localhost:4318/)
name Names to greet (default: "World")`,
);
process.exit(0);
} else {
names.push(args[i]);
}
}
if (names.length === 0) {
names.push("World");
}
return { otelEndpoint, names };
}
function setupSDK(endpoint: string): NodeSDK {
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: "otel-sample",
[ATTR_SERVICE_VERSION]: "1.0",
}),
traceExporter: new OTLPTraceExporter({
url: new URL("/v1/traces", endpoint).toString(),
}),
});
sdk.start();
return sdk;
}
// setupSDK() を呼び出す前に tracer を取得しても、SDK の start() 後に SDK で登録されたトレーサープロバイダーが使用される
const tracer = trace.getTracer("otel-sample", "1.0");
async function greet(name: string): Promise<void> {
await tracer.startActiveSpan(`greet:${name}`, async (span) => {
const delaySec = Math.random() * 2;
span.setAttribute("delay_sec", delaySec);
span.setAttribute("name", name);
await new Promise((resolve) => setTimeout(resolve, delaySec * 1000));
console.log(`Hello, ${name}!`);
if (delaySec > 1) {
const err = new Error(`Too slow: ${delaySec.toFixed(2)}s`);
span.recordException(err);
span.setStatus({
code: SpanStatusCode.ERROR,
message: err.message,
});
span.end();
throw err;
}
span.end();
});
}
async function greets(names: string[]): Promise<void> {
await tracer.startActiveSpan("greets", async (span) => {
let hasError = false;
for (const name of names) {
try {
await greet(name);
} catch {
console.log(`Failed to hello ${name}`);
hasError = true;
}
}
if (hasError) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: "Some greetings failed",
});
}
span.end();
});
}
function finish(): void {
tracer.startActiveSpan("finish", (span) => {
console.log("Done!");
span.end();
});
}
async function cliRun(names: string[]): Promise<void> {
await tracer.startActiveSpan("cli-run", async (span) => {
span.setAttribute("args.names", names.join(", "));
try {
await greets(names);
finish();
} catch (err) {
if (err instanceof Error) {
span.recordException(err);
}
span.setStatus({
code: SpanStatusCode.ERROR,
message: err instanceof Error ? err.message : String(err),
});
throw err;
} finally {
span.end();
}
});
}
async function main() {
const parsed = parseArgs(process.argv);
const sdk = parsed.otelEndpoint ? setupSDK(parsed.otelEndpoint) : null;
try {
await cliRun(parsed.names);
} finally {
if (sdk) {
// トレースの送信を保証する
await sdk.shutdown();
}
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment