Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/tracing / src / trace.ts
Size: Mime:
import { EMPTY_OBJ, isObj } from 'exotic'
import { TRACING_HEADERS } from './toTracing'
import { LoggingMetaType, LoggingMetaTracingType, LoggingMetaMetaType } from './typings'
import { Timer } from './Timer'
// import { fetchDynamic } from './fetch'

// here just because this is for dev only...
// @todo if tracing is disabled, export nothing...
// @todo ^ same for timer
export const TRACE_URL = process.env.TRACE_URL || 'http://localhost:7777/trace'
export const SHOULD_CALL_TRACING_ENDPOINT = process.env.SHOULD_CALL_TRACING_ENDPOINT || 'true'

export type TracingHeadersType = Record<string, string | number | boolean>

/**
 * @todo use `raf` and batch requests
 */
export interface TraceArgsType {
  appName: string
  spanName?: string
  url: string
  //  | number | boolean
  headers: TracingHeadersType
  timer: Timer

  tracing?: LoggingMetaTracingType
  meta?: LoggingMetaType
}

// x-session-id => sessionId
export function fromHeadersToTracing(headers: TracingHeadersType) {
  return {
    // ...headers,
    openTraceId: headers[TRACING_HEADERS.openTraceId] as string,
    openTraceSpanId: headers[TRACING_HEADERS.openTraceSpanId] as number,
    requestId: headers[TRACING_HEADERS.requestId] as string,
    sessionId: headers[TRACING_HEADERS.sessionId] as string,
  }
}

// here, we are using meta > req
export function ensureTracingHeaders(headers: LoggingMetaTracingType, tracing: LoggingMetaTracingType) {
  return {
    openTraceId: tracing.openTraceId || headers.openTraceId,
    openTraceSpanId: tracing.openTraceSpanId || headers.openTraceSpanId,
    sessionId: tracing.sessionId || headers.sessionId,
    requestId: tracing.requestId || headers.requestId,
  }
}

export const DEFAULT_EMPTY_META = Object.freeze({
  tracing: {},
})

function toOptions(options: TraceArgsType = EMPTY_OBJ as any): TraceArgsType {
  const { tracing, meta, ...remainingProps } = options

  // @@perf @todo
  const metaOrMergedMeta = meta || { tracing: tracing || EMPTY_OBJ, meta: EMPTY_OBJ } as LoggingMetaType

  return  { ...remainingProps, meta: metaOrMergedMeta, tracing: tracing || metaOrMergedMeta.tracing }
}

export function trace(options: TraceArgsType) {
  if (SHOULD_CALL_TRACING_ENDPOINT === 'false') {
    console.trace('[tracing] not calling tracing endpoint due to env')
    return undefined
  }

  const coerced = toOptions(options)
  const { url, headers, timer, appName, spanName, tracing, meta } = coerced
  // console.log('___trace___', coerced)

  if (isObj(timer.endWallTime) === false) {
    timer.end()
  }
  const tracingUnsafe = fromHeadersToTracing(headers)
  const tracingFinal = ensureTracingHeaders(tracingUnsafe, tracing)
  const sizing = meta.meta

  const body: LoggingMetaType = {
    meta: {
      bytesReceived: sizing.bytesReceived,
      bytesSent: sizing.bytesSent,
      spanName,
      appName,
      url,
    },
    tracing: tracingFinal,
    cache: {
      isCached: false,
    },
    timing: timer.toJSON(),
  }

  // console.debug(`[tracing] trace: ${url}`)

  return fetch(TRACE_URL, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    redirect: 'follow',
    referrer: 'no-referrer',
    body: JSON.stringify(body),
  })
  .catch(rejectionException => {
    console.error('[tracing] likely the tracing endpoint is not up')
  })
  .finally(() => {
    console.debug(`traced ${url}`)
    // console.log(body)
  })
}