responseTransform.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import {
  2. DataFrame,
  3. DataSourceInstanceSettings,
  4. FieldType,
  5. MutableDataFrame,
  6. TraceLog,
  7. TraceSpanRow,
  8. } from '@grafana/data';
  9. import { transformTraceData } from '@jaegertracing/jaeger-ui-components';
  10. import { JaegerResponse, Span, TraceProcess, TraceResponse } from './types';
  11. export function createTraceFrame(data: TraceResponse): DataFrame {
  12. const spans = data.spans.map((s) => toSpanRow(s, data.processes));
  13. const frame = new MutableDataFrame({
  14. fields: [
  15. { name: 'traceID', type: FieldType.string },
  16. { name: 'spanID', type: FieldType.string },
  17. { name: 'parentSpanID', type: FieldType.string },
  18. { name: 'operationName', type: FieldType.string },
  19. { name: 'serviceName', type: FieldType.string },
  20. { name: 'serviceTags', type: FieldType.other },
  21. { name: 'startTime', type: FieldType.number },
  22. { name: 'duration', type: FieldType.number },
  23. { name: 'logs', type: FieldType.other },
  24. { name: 'tags', type: FieldType.other },
  25. { name: 'warnings', type: FieldType.other },
  26. { name: 'stackTraces', type: FieldType.other },
  27. ],
  28. meta: {
  29. preferredVisualisationType: 'trace',
  30. custom: {
  31. traceFormat: 'jaeger',
  32. },
  33. },
  34. });
  35. for (const span of spans) {
  36. frame.add(span);
  37. }
  38. return frame;
  39. }
  40. function toSpanRow(span: Span, processes: Record<string, TraceProcess>): TraceSpanRow {
  41. return {
  42. spanID: span.spanID,
  43. traceID: span.traceID,
  44. parentSpanID: span.references?.find((r) => r.refType === 'CHILD_OF')?.spanID,
  45. operationName: span.operationName,
  46. // from micro to millis
  47. startTime: span.startTime / 1000,
  48. duration: span.duration / 1000,
  49. logs: span.logs.map((l) => ({
  50. ...l,
  51. timestamp: l.timestamp / 1000,
  52. })),
  53. tags: span.tags,
  54. warnings: span.warnings ?? undefined,
  55. stackTraces: span.stackTraces,
  56. serviceName: processes[span.processID].serviceName,
  57. serviceTags: processes[span.processID].tags,
  58. };
  59. }
  60. export function createTableFrame(data: TraceResponse[], instanceSettings: DataSourceInstanceSettings): DataFrame {
  61. const frame = new MutableDataFrame({
  62. fields: [
  63. {
  64. name: 'traceID',
  65. type: FieldType.string,
  66. config: {
  67. unit: 'string',
  68. displayNameFromDS: 'Trace ID',
  69. links: [
  70. {
  71. title: 'Trace: ${__value.raw}',
  72. url: '',
  73. internal: {
  74. datasourceUid: instanceSettings.uid,
  75. datasourceName: instanceSettings.name,
  76. query: {
  77. query: '${__value.raw}',
  78. },
  79. },
  80. },
  81. ],
  82. },
  83. },
  84. { name: 'traceName', type: FieldType.string, config: { displayNameFromDS: 'Trace name' } },
  85. { name: 'startTime', type: FieldType.time, config: { displayNameFromDS: 'Start time' } },
  86. { name: 'duration', type: FieldType.number, config: { displayNameFromDS: 'Duration', unit: 'µs' } },
  87. ],
  88. meta: {
  89. preferredVisualisationType: 'table',
  90. },
  91. });
  92. // Show the most recent traces
  93. const traceData = data.map(transformToTraceData).sort((a, b) => b?.startTime! - a?.startTime!);
  94. for (const trace of traceData) {
  95. frame.add(trace);
  96. }
  97. return frame;
  98. }
  99. function transformToTraceData(data: TraceResponse) {
  100. const traceData = transformTraceData(data);
  101. if (!traceData) {
  102. return;
  103. }
  104. return {
  105. traceID: traceData.traceID,
  106. startTime: traceData.startTime / 1000,
  107. duration: traceData.duration,
  108. traceName: traceData.traceName,
  109. };
  110. }
  111. export function transformToJaeger(data: MutableDataFrame): JaegerResponse {
  112. let traceResponse: TraceResponse = {
  113. traceID: '',
  114. spans: [],
  115. processes: {},
  116. warnings: null,
  117. };
  118. let processes: string[] = [];
  119. for (let i = 0; i < data.length; i++) {
  120. const span = data.get(i);
  121. // Set traceID
  122. if (!traceResponse.traceID) {
  123. traceResponse.traceID = span.traceID;
  124. }
  125. // Create process if doesn't exist
  126. if (!processes.find((p) => p === span.serviceName)) {
  127. processes.push(span.serviceName);
  128. traceResponse.processes[`p${processes.length}`] = {
  129. serviceName: span.serviceName,
  130. tags: span.serviceTags,
  131. };
  132. }
  133. // Create span
  134. traceResponse.spans.push({
  135. traceID: span.traceID,
  136. spanID: span.spanID,
  137. duration: span.duration * 1000,
  138. references: span.parentSpanID
  139. ? [
  140. {
  141. refType: 'CHILD_OF',
  142. spanID: span.parentSpanID,
  143. traceID: span.traceID,
  144. },
  145. ]
  146. : [],
  147. flags: 0,
  148. logs: span.logs.map((l: TraceLog) => ({
  149. ...l,
  150. timestamp: l.timestamp * 1000,
  151. })),
  152. operationName: span.operationName,
  153. processID:
  154. Object.keys(traceResponse.processes).find(
  155. (key) => traceResponse.processes[key].serviceName === span.serviceName
  156. ) || '',
  157. startTime: span.startTime * 1000,
  158. tags: span.tags,
  159. warnings: span.warnings ? span.warnings : null,
  160. });
  161. }
  162. return { data: [traceResponse], total: 0, limit: 0, offset: 0, errors: null };
  163. }