Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Importing OTLP "File Exporter" JSON fails #2225

Open
justfalter opened this issue Mar 22, 2024 · 9 comments · May be fixed by #2254
Open

[Bug]: Importing OTLP "File Exporter" JSON fails #2225

justfalter opened this issue Mar 22, 2024 · 9 comments · May be fixed by #2254

Comments

@justfalter
Copy link

What happened?

jaegertracing/all-in-one 1.55 fails to import JSON generated by OTEL File Exporter.

Steps to reproduce

  1. Spin up jaegertracing/all-in-one 1.55
  2. Save trace example JSON data to test.json
{"resourceSpans":[{"resource":{"attributes":[{"key":"resource-attr","value":{"stringValue":"resource-attr-val-1"}}]},"scopeSpans":[{"scope":{},"spans":[{"traceId":"","spanId":"","parentSpanId":"","name":"operationA","startTimeUnixNano":"1581452772000000321","endTimeUnixNano":"1581452773000000789","droppedAttributesCount":1,"events":[{"timeUnixNano":"1581452773000000123","name":"event-with-attr","attributes":[{"key":"span-event-attr","value":{"stringValue":"span-event-attr-val"}}],"droppedAttributesCount":2},{"timeUnixNano":"1581452773000000123","name":"event","droppedAttributesCount":2}],"droppedEventsCount":1,"status":{"message":"status-cancelled","code":2}},{"traceId":"","spanId":"","parentSpanId":"","name":"operationB","startTimeUnixNano":"1581452772000000321","endTimeUnixNano":"1581452773000000789","links":[{"traceId":"","spanId":"","attributes":[{"key":"span-link-attr","value":{"stringValue":"span-link-attr-val"}}],"droppedAttributesCount":4},{"traceId":"","spanId":"","droppedAttributesCount":1}],"droppedLinksCount":3,"status":{}}]}]}]}
{"resourceSpans":[{"resource":{"attributes":[{"key":"resource-attr","value":{"stringValue":"resource-attr-val-1"}}]},"scopeSpans":[{"scope":{},"spans":[{"traceId":"","spanId":"","parentSpanId":"","name":"operationA","startTimeUnixNano":"1581452772000000321","endTimeUnixNano":"1581452773000000789","droppedAttributesCount":1,"events":[{"timeUnixNano":"1581452773000000424","name":"event-with-attr","attributes":[{"key":"span-event-attr","value":{"stringValue":"span-event-attr-val"}}],"droppedAttributesCount":2},{"timeUnixNano":"1581452773000000424","name":"event","droppedAttributesCount":2}],"droppedEventsCount":1,"status":{"message":"status-cancelled","code":2}},{"traceId":"","spanId":"","parentSpanId":"","name":"operationB","startTimeUnixNano":"1581452772000000343","endTimeUnixNano":"1581452773000001089","links":[{"traceId":"","spanId":"","attributes":[{"key":"span-link-attr","value":{"stringValue":"span-link-attr-val"}}],"droppedAttributesCount":3},{"traceId":"","spanId":"","droppedAttributesCount":4}],"droppedLinksCount":2,"status":{}}]}]}]}
{"resourceSpans":[{"resource":{"attributes":[{"key":"resource-attr","value":{"stringValue":"resource-attr-val-1"}}]},"scopeSpans":[{"scope":{},"spans":[{"traceId":"","spanId":"","parentSpanId":"","name":"operationA","startTimeUnixNano":"1581452772000000321","endTimeUnixNano":"1581452773000000789","droppedAttributesCount":1,"events":[{"timeUnixNano":"1581452773000000826","name":"event-with-attr","attributes":[{"key":"span-event-attr","value":{"stringValue":"span-event-attr-val"}}],"droppedAttributesCount":2},{"timeUnixNano":"1581452773000000826","name":"event","droppedAttributesCount":2}],"droppedEventsCount":1,"status":{"message":"status-cancelled","code":2}},{"traceId":"","spanId":"","parentSpanId":"","name":"operationB","startTimeUnixNano":"1581452772000200521","endTimeUnixNano":"1581452773000004789","links":[{"traceId":"","spanId":"","attributes":[{"key":"span-link-attr","value":{"stringValue":"span-link-attr-val"}}],"droppedAttributesCount":5},{"traceId":"","spanId":"","droppedAttributesCount":2}],"droppedLinksCount":3,"status":{}}]}]}]}
{"resourceSpans":[{"resource":{"attributes":[{"key":"resource-attr","value":{"stringValue":"resource-attr-val-1"}}]},"scopeSpans":[{"scope":{},"spans":[{"traceId":"","spanId":"","parentSpanId":"","name":"operationA","startTimeUnixNano":"1581452772000000321","endTimeUnixNano":"1581452773000000789","droppedAttributesCount":1,"events":[{"timeUnixNano":"1581452773000010925","name":"event-with-attr","attributes":[{"key":"span-event-attr","value":{"stringValue":"span-event-attr-val"}}],"droppedAttributesCount":2},{"timeUnixNano":"1581452773000010925","name":"event","droppedAttributesCount":2}],"droppedEventsCount":1,"status":{"message":"status-cancelled","code":2}},{"traceId":"","spanId":"","parentSpanId":"","name":"operationB","startTimeUnixNano":"1581452772000011821","endTimeUnixNano":"1581452772000012924","links":[{"traceId":"","spanId":"","attributes":[{"key":"span-link-attr","value":{"stringValue":"span-link-attr-val"}}],"droppedAttributesCount":2},{"traceId":"","spanId":"","droppedAttributesCount":2}],"droppedLinksCount":5,"status":{}}]}]}]}
  1. Drag test.json onto Jaeger's Click or drag files to this area.
  2. Get error:
There was an error querying for traces:
Error parsing JSON: JSON.parse: unexpected non-whitespace character after JSON data at line 2 column 1 of the JSON data

Expected behavior

I expect Jaeger to have loaded successfully loaded the Open Telemetry traces, per jaegertracing/jaeger#4949

Relevant log output

No response

Screenshot

image

Additional context

The existing code only expects there to be a single JSON object entry in the OTEL File Exporter JSON output, but the file exporter specification says that the is in JSON lines format (file contains multiple JSON serialized objects, with a new-line separating each).

This is evident when looking at the test data in the original PR, as it is only a single JSON object entry.
https://github.com/jaegertracing/jaeger/pull/5155/files

Jaeger backend version

1.55

SDK

No response

Pipeline

Example of the opentelemetry collector configuration I use to capture OTEL traces to OTEL file-exporter format:

extensions:
  zpages:
    endpoint: 0.0.0.0:55679

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
  memory_limiter:
    # 75% of maximum memory up to 2G
    limit_mib: 1536
    # 25% of limit up to 2G
    spike_limit_mib: 512
    check_interval: 5s

exporters:
  file:
    path: /output/otel.json

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [file]

  extensions: [zpages]

Stogage backend

No response

Operating system

No response

Deployment model

No response

Deployment configs

No response

@justfalter justfalter added the bug label Mar 22, 2024
@yurishkuro
Copy link
Member

Two issues with your data:

  1. The input file is expected to be a single JSON object:
{
  "resourceSpans": ...
}

You have multiple objects (one per line) which do not have an equivalent representation in OTLP data types (it would be an array of TraceData object). It sounds like a problem worth solving.

  1. All the trace/span IDs in the JSON are empty strings, which are not valid. So even if the structure was correct (you can try submitting just one line of that file), I suspect the parsing will then choke on the IDs.

@justfalter
Copy link
Author

  1. The input file is expected to be a single JSON object:

The implementation expects it to be a single JSON object, but the specification linked to by the origin feature request (#4949) states that the file is supposed to be in "JSON lines" format.

JSON lines file
This file is a JSON lines file (jsonlines.org), and therefore follows those requirements:

  • UTF-8 encoding
  • Each line is a valid JSON value
  • The line separator is \n
  • The preferred file extension is jsonl

When I use the current file-exporter (https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.96.0), it generates a file in "JSON lines" format.

  1. All the trace/span IDs in the JSON are empty strings, which are not valid. So even if the structure was correct (you can try submitting just one line of that file), I suspect the parsing will then choke on the IDs.

In order to avoid this as a distraction, I'm including a trace that I've generated using opentelemetry's js SDK.


The opentelemetry collector contrib configuration:

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:
    # The following ensures that one span is written per line.
    send_batch_size: 1
    send_batch_max_size: 1

exporters:
  file:
    path: /output/otel.json

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [file]

The generated trace otel.json. Note that there are 4 liness, one JSON object per line.

I can use the following jq to process the file into a single JSON object. The following concatenates all of the .resourceSpans arrays across each JSON object (per line), adding them to a single JSON object that Jaeger currently expects:

jq -c --slurp '{resourceSpans: map(.resourceSpans[])}' < otel.json > otel-out.json

Which gives me otel-out.json.


My expectation is that if Jaeger says that it will import OpenTelemetry JSON generated by the opentelemetry collector's file-export, then it should be able to handle a multi-line JSON file.

@NavinShrinivas
Copy link
Contributor

A initial proposal: what if we get the frontend to add a [] and commas in between these json objects and loop over them with the same existing logic. Would this be a viable solution?

@varshith257
Copy link
Contributor

@yurishkuro Any guidance on fixing it will be more helpful

@yurishkuro
Copy link
Member

I will move to UI

@yurishkuro yurishkuro transferred this issue from jaegertracing/jaeger Mar 23, 2024
@yurishkuro
Copy link
Member

The current code that checks for OTLP format is here:

if ('resourceSpans' in traceObj) {

It effectively checks for JSON like {"resourceSpans":[...]}. I would suggest extending that code to:

  1. also recognize[{"resourceSpans":[arr1]}, ... {"resourceSpans":[arrN]}]
  2. join resourceSpans arrays into a single {"resourceSpans":[arr1..., arrN]} which then should be handled normally by the existing code

@Adarsh-jaiss
Copy link

  • Hey @yurishkuro , I would like to work on this issue :)

@sfc-gh-kbregula
Copy link

sfc-gh-kbregula commented May 22, 2024

I need this too. For now, I'm using a script like this that allows me to send all traces from a local file to Jagear.

import requests
import json
import argparse
from pathlib import Path

def send_post_request(url, json_data):
    headers = {'Content-Type': 'application/json'}
    response = requests.post(url, data=json.dumps(json_data), headers=headers)
    response.raise_for_status()
    return response


def process_file(input_file: Path, server_url: str):
    lines = input_file.read_text().splitlines()
    api_url = server_url.rstrip("/") + "/v1/traces"
    for line in lines:
        json_data = json.loads(line.strip())
        send_post_request(api_url, json_data)
    print("Finished")

def main():
    parser = argparse.ArgumentParser(description="Send saves traces to OLTP collector")
    parser.add_argument(
        '--server-url',
        type=str,
        default='http://localhost:4318',
        help="The server URL to send the POST requests to."
    )
    parser.add_argument('input_file', type=Path, help="The JSONL file to be processed.")

    args = parser.parse_args()

    process_file(args.input_file, args.server_url)


if __name__ == "__main__":
    main()

@yurishkuro
Copy link
Member

@sfc-gh-kbregula if you're proficient in JS consider if you can bring #2254 over the finish line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants