opentelemetry-js icon indicating copy to clipboard operation
opentelemetry-js copied to clipboard

SpanShim Log Removes Object (NonPrimitive) Properties

Open fallenmanrisesup opened this issue 3 years ago • 4 comments
trafficstars

What happened?

Steps to Reproduce

https://github.com/open-telemetry/opentelemetry-js/tree/a8047ba9cdda65292778614ec9a1f8a2045faa9f/packages/opentelemetry-shim-opentracing

I call span.log({ event: 'ss', payload: { body: '...' }, strField: 'field' });

Expected Result

See payload as a json in Jaeger UI

Actual Result

The field was removed from log entry

Additional Details

OpenTelemetry Setup Code

const tracer = new TracerShim(
    opentelemetry.api.trace.getTracer(packageJSON.name, packageJSON.version)
  );

const span = tracer.startSpan('TEST');
const payload = {
  doenstGoToLog: false,
};

span.log({ event: 'ss', payload, ok: true});

span.finish(); // Then only 'event' and 'ok' appears in span log

package.json

{   
    "@opentelemetry/api": "1.1.0",
    "@opentelemetry/api-metrics": "0.29.0",
    "@opentelemetry/core": "1.2.0",
    "@opentelemetry/exporter-metrics-otlp-http": "0.29.0",
    "@opentelemetry/exporter-prometheus": "^0.29.0",
    "@opentelemetry/exporter-trace-otlp-http": "0.29.0",
    "@opentelemetry/propagator-b3": "1.3.1",
    "@opentelemetry/propagator-jaeger": "1.3.1",
    "@opentelemetry/resources": "1.2.0",
    "@opentelemetry/sdk-metrics-base": "^0.29.0",
    "@opentelemetry/sdk-node": "0.29.0",
    "@opentelemetry/semantic-conventions": "1.3.0"
}

Relevant log output

No response

fallenmanrisesup avatar Sep 06 '22 17:09 fallenmanrisesup

For now I use this workaround (I'll create a PR with more clear ts code if u guys agree)

const isPrimitiveAttributeValue = (val) =>
  ['number', 'string', 'boolean'].includes(typeof val);

const serializeRecursive = (o, isRootCall = true) => {
  if (isPrimitiveAttributeValue(o)) return o;

  if (Array.isArray(o)) {
    return isRootCall
      ? o.map((x) => serializeRecursive(x, false))
      : JSON.stringify(o);
  }

  if (typeof o === 'object') return JSON.stringify(o);

  return null;
};

const serializeSpanAttributeValue = (o) => serializeRecursive(o);

const serializeSpanAttribute = (o) =>
  Object.entries(o).reduce((prev, [k, v]) => {
    prev[k] = serializeSpanAttributeValue(v);
    return prev;
  }, {});

class ExtendedSpanShim extends SpanShim {
  log(obj, ts) {
    super.log(serializeSpanAttribute(obj), ts);
  }
}

class ExtendedTracerShim extends TracerShim {
  startSpan(name, options) {
    const span = super.startSpan(name, options);

    return new ExtendedSpanShim(
      this,
      span.getSpan(),
      span.context().getBaggage()
    );
  }
}

const tracer = new ExtendedTracerShim(
    opentelemetry.api.trace.getTracer(packageJSON.name, packageJSON.version)
  );
// ...

fallenmanrisesup avatar Sep 06 '22 17:09 fallenmanrisesup

For now I use this workaround (I'll create a PR with more clear ts code if u guys agree)

const isPrimitiveAttributeValue = (val) =>
  ['number', 'string', 'boolean'].includes(typeof val);

const serializeRecursive = (o, isRootCall = true) => {
  if (isPrimitiveAttributeValue(o)) return o;

  if (Array.isArray(o)) {
    return isRootCall
      ? o.map((x) => serializeRecursive(x, false))
      : JSON.stringify(o);
  }

  if (typeof o === 'object') return JSON.stringify(o);

  return null;
};

const serializeSpanAttributeValue = (o) => serializeRecursive(o);

const serializeSpanAttribute = (o) =>
  Object.entries(o).reduce((prev, [k, v]) => {
    prev[k] = serializeSpanAttributeValue(v);
    return prev;
  }, {});

class ExtendedSpanShim extends SpanShim {
  log(obj, ts) {
    super.log(serializeSpanAttribute(obj), ts);
  }
}

class ExtendedTracerShim extends TracerShim {
  startSpan(name, options) {
    const span = super.startSpan(name, options);

    return new ExtendedSpanShim(
      this,
      span.getSpan(),
      span.context().getBaggage()
    );
  }
}

const tracer = new ExtendedTracerShim(
    opentelemetry.api.trace.getTracer(packageJSON.name, packageJSON.version)
  );
// ...

Thanks for the workaround example it helps to explain the situation that is happening too. I'm curious why you would have the recursive serialize and not just call JSON.stringify at the start? Can you give an example expected output format?

dyladan avatar Sep 06 '22 18:09 dyladan

For now I use this workaround (I'll create a PR with more clear ts code if u guys agree)

const isPrimitiveAttributeValue = (val) =>
  ['number', 'string', 'boolean'].includes(typeof val);

const serializeRecursive = (o, isRootCall = true) => {
  if (isPrimitiveAttributeValue(o)) return o;

  if (Array.isArray(o)) {
    return isRootCall
      ? o.map((x) => serializeRecursive(x, false))
      : JSON.stringify(o);
  }

  if (typeof o === 'object') return JSON.stringify(o);

  return null;
};

const serializeSpanAttributeValue = (o) => serializeRecursive(o);

const serializeSpanAttribute = (o) =>
  Object.entries(o).reduce((prev, [k, v]) => {
    prev[k] = serializeSpanAttributeValue(v);
    return prev;
  }, {});

class ExtendedSpanShim extends SpanShim {
  log(obj, ts) {
    super.log(serializeSpanAttribute(obj), ts);
  }
}

class ExtendedTracerShim extends TracerShim {
  startSpan(name, options) {
    const span = super.startSpan(name, options);

    return new ExtendedSpanShim(
      this,
      span.getSpan(),
      span.context().getBaggage()
    );
  }
}

const tracer = new ExtendedTracerShim(
    opentelemetry.api.trace.getTracer(packageJSON.name, packageJSON.version)
  );
// ...

Thanks for the workaround example it helps to explain the situation that is happening too. I'm curious why you would have the recursive serialize and not just call JSON.stringify at the start? Can you give an example expected output format?

I use recursive serialization because in /shim.ts there are some conditions that made me think it's possible to use arrays in log fields, that's why I added extra serialization

But I'll add just JSON.stringify in my pr. Do u agree with it?

fallenmanrisesup avatar Sep 07 '22 06:09 fallenmanrisesup

@dyladan #3236

fallenmanrisesup avatar Sep 07 '22 11:09 fallenmanrisesup