Skip to content

Commit

Permalink
Modify base command to support nested telemetry spans
Browse files Browse the repository at this point in the history
  • Loading branch information
pziemkowski committed Sep 14, 2023
1 parent eecfd74 commit 587b032
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
57 changes: 43 additions & 14 deletions packages/internal/cli/src/baseCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Command, Flags, Interfaces } from '@oclif/core';
import { ExitError } from '@oclif/core/lib/errors';
import { Span, SpanStatusCode, trace, Tracer } from '@opentelemetry/api';

import { SB_TELEMETRY_DISABLED } from './config/env';
import { traceExporter } from './config/telemetry';
import * as telemetry from './config/telemetry';

const formatAttrs = (obj: { [k: string]: string } = {}, prefix = '') => {
return Object.fromEntries(
Expand Down Expand Up @@ -38,21 +37,52 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
this.flags = flags as Flags<T>;
this.args = args as Args<T>;

if (!SB_TELEMETRY_DISABLED) {
if (telemetry.isEnabled) {
this.printTelemetryInfo();
this.tracer = trace.getTracer('command', this.config.version);
}
}

this.span = this.tracer.startSpan(`command.${this.ctor.id}`, {
attributes: {
...formatAttrs(flags, 'flags'),
...formatAttrs(args, 'args'),
},
});
async _run<T>() {
let err;
let result;
try {
// remove redirected env var to allow subsessions to run autoupdated client
// @ts-ignore
this.removeEnvVar('REDIRECTED');
await this.init();

if (telemetry.isEnabled && this.tracer) {
result = this.tracer.startActiveSpan(
`command.${this.ctor.id}`,
{
attributes: {
...formatAttrs(this.flags, 'flags'),
...formatAttrs(this.args, 'args'),
},
},
async (span) => {
this.span = span;
const _result = this.run();
span.end();
return _result;
}
);
} else {
result = await this.run();
}
} catch (error: any) {
err = error;
await this.catch(error);
} finally {
await this.finally(err);
}
if (result && this.jsonEnabled()) this.logJson(this.toSuccessJson(result));
return result;
}

protected async catch(err: Error & { exitCode?: number }): Promise<any> {
if (!SB_TELEMETRY_DISABLED) {
if (telemetry.isEnabled) {
if (!(err instanceof ExitError) || err.oclif.exit !== 0) {
this.span?.addEvent('Command error');
this.span?.recordException(err);
Expand All @@ -63,21 +93,20 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
}

protected async finally(_: Error | undefined): Promise<any> {
if (!SB_TELEMETRY_DISABLED) {
if (telemetry.isEnabled) {
this.span?.addEvent('Command finished');
this.span?.end();

// Need to wait en event loop for the internal promise in exporter to be visible
await new Promise((resolve) => setTimeout(() => resolve(true)));
// wait for the exporter to send data
await traceExporter.forceFlush();
await telemetry.traceExporter.forceFlush();
}
return super.finally(_);
}

protected printTelemetryInfo(): void {
console.log({SB_TELEMETRY_DISABLED})
if (!SB_TELEMETRY_DISABLED) {
if (telemetry.isEnabled) {
this.log(`\x1b[2m
------ Notice ------
This CLI collects various anonymous events, warnings, and errors to improve the CLI tool and enhance your user experience.
Expand Down
4 changes: 4 additions & 0 deletions packages/internal/cli/src/config/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';

import { SB_TELEMETRY_DISABLED } from './env';

const sbTelemetry = require('@apptension/saas-boilerplate-telemetry');

export const isEnabled = !SB_TELEMETRY_DISABLED;

export const traceExporter = new OTLPTraceExporter({
url: sbTelemetry?.[0],
headers: {
Expand Down

0 comments on commit 587b032

Please sign in to comment.