> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siftstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stream data to Sift from FlexLogger

> Install the Sift FlexLogger plugins to send Channel data to Sift.

export const MintTable = ({columns = [], rows = [], columnWidths = []}) => {
  const pushTextWithLineBreaks = (parts, text, keyBase) => {
    const segments = String(text).split(/\\n|\n/);
    segments.forEach((segment, idx) => {
      if (segment) {
        parts.push(<span key={`${keyBase}-text-${idx}`}>{segment}</span>);
      }
      if (idx < segments.length - 1) {
        parts.push(<br key={`${keyBase}-br-${idx}`} />);
      }
    });
  };
  const parseMarkdown = text => {
    if (text === null || text === undefined) return "";
    const str = String(text);
    const parts = [];
    let lastIndex = 0;
    const pattern = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[([^\]]+)\]\(([^)]+)\))/g;
    let match;
    while (true) {
      match = pattern.exec(str);
      if (match === null) {
        break;
      }
      if (match.index > lastIndex) {
        pushTextWithLineBreaks(parts, str.substring(lastIndex, match.index), `before-${lastIndex}`);
      }
      const fullMatch = match[0];
      if (fullMatch.startsWith("`") && fullMatch.endsWith("`")) {
        parts.push(<code key={match.index}>{fullMatch.slice(1, -1)}</code>);
      } else if (fullMatch.startsWith("**") && fullMatch.endsWith("**")) {
        parts.push(<strong key={match.index}>{fullMatch.slice(2, -2)}</strong>);
      } else if (fullMatch.startsWith("*") && fullMatch.endsWith("*")) {
        parts.push(<em key={match.index}>{fullMatch.slice(1, -1)}</em>);
      } else if (fullMatch.startsWith("[")) {
        const linkText = match[2];
        const linkUrl = match[3];
        parts.push(<a key={match.index} href={linkUrl} className="text-black-600 dark:text-black-400">
            {linkText}
          </a>);
      }
      lastIndex = pattern.lastIndex;
    }
    if (lastIndex < str.length) {
      pushTextWithLineBreaks(parts, str.substring(lastIndex), `tail-${lastIndex}`);
    }
    if (parts.length > 0) {
      return parts;
    }
    const plainParts = [];
    pushTextWithLineBreaks(plainParts, str, "plain");
    return plainParts.length ? plainParts : str;
  };
  const safeColumns = Array.isArray(columns) ? columns : [];
  const safeRows = Array.isArray(rows) ? rows : [];
  const safeColumnWidths = Array.isArray(columnWidths) ? columnWidths : [];
  const hasColumnWidths = safeColumnWidths.some(w => w !== null && w !== undefined && w !== "");
  const toCssWidth = width => typeof width === "number" ? `${width}px` : String(width);
  const getColumnStyle = idx => {
    const rawWidth = safeColumnWidths[idx];
    if (rawWidth === null || rawWidth === undefined || rawWidth === "") {
      return undefined;
    }
    const width = toCssWidth(rawWidth);
    return {
      width,
      minWidth: width
    };
  };
  const containerStyle = hasColumnWidths ? undefined : {
    overflowX: "auto"
  };
  const tableStyle = hasColumnWidths ? {
    tableLayout: "fixed",
    width: "100%"
  } : {
    width: "max-content",
    minWidth: "100%"
  };
  if (!Array.isArray(columns) || !Array.isArray(rows) || !Array.isArray(columnWidths)) {
    console.warn("MintTable received invalid props:", {
      columns,
      rows,
      columnWidths
    });
  }
  if (!safeColumns.length && !safeRows.length) {
    return null;
  }
  return <div className="mint-table-container" style={containerStyle}>
      <table style={tableStyle}>
        {hasColumnWidths && <colgroup>
            {safeColumns.map((_, idx) => {
    const style = getColumnStyle(idx);
    return <col key={idx} style={style} />;
  })}
          </colgroup>}
        <thead>
          <tr>
            {safeColumns.map((col, idx) => <th key={idx} className="text-left" style={getColumnStyle(idx)}>
                <b>{parseMarkdown(col)}</b>
              </th>)}
          </tr>
        </thead>
        <tbody>
          {safeRows.map((row, rIdx) => {
    const safeRow = Array.isArray(row) ? row : [];
    return <tr key={rIdx}>
                {safeRow.map((cell, cIdx) => <td key={cIdx} style={getColumnStyle(cIdx)}>
                    {parseMarkdown(cell)}
                  </td>)}
              </tr>;
  })}
        </tbody>
      </table>
    </div>;
};

After completing this workflow, FlexLogger telemetry is available in Sift for analysis, either streamed live during a test or uploaded automatically once a test completes.

## Before you begin

* FlexLogger 2024 Q4 or above
* API access [set up](/documentation/manage/set-up-api-access) with an API key and the REST and gRPC base URLs for your Sift environment
* Optional: [Set environment variables](#set-environment-variables) on the machine running FlexLogger to pre-fill plugin configuration fields

## Set environment variables

Setting these Windows environment variables before adding either plugin to a FlexLogger project automatically populates the corresponding plugin settings, so you do not need to enter them manually in each plugin's configuration dialog.

<MintTable
  columns={['Variable', 'Populates']}
  columnWidths={['40%', '60%']}
  rows={[
    ['SIFT_API_GRPC_URL', 'Sift API gRPC URL'],
    ['SIFT_API_REST_URL', 'Sift API REST URL'],
    ['SIFT_API_KEY', 'Sift API Key'],
]}
/>

## How the FlexLogger plugins work

Installing the Sift FlexLogger plugin package adds two ways to send data to Sift:

* Stream Channel data to Sift live, as a test runs (Sift Stream)
* Upload a TDMS file to Sift once a run stops (Sift TDMS Uploader)

## Install the Sift FlexLogger plugins

1. Go to the [sift-labview Releases page](https://github.com/sift-stack/sift-labview/releases) and find the most recent release tagged `flexlogger-plugins`. Download `flexlogger_dist.zip` from that release.
2. Unzip the file.
3. Run `install_flexlogger_plugins.bat`.

## Stream live data to Sift

1. In **FlexLogger**, configure the Channels you want to stream.
2. Select **Add channels > Plug-in > Sift Stream**.
3. Click the configure (gear) icon on the Sift Stream plugin.
4. If you [set the environment variables](#set-environment-variables) beforehand, the fields are pre-filled. Otherwise, fill out the plugin settings.

   <MintTable
     columns={['Field', 'Description']}
     columnWidths={['25%', '75%']}
     rows={[
    ['Asset Name', 'The name of the Sift Asset to stream data to.'],
    ['Sift API gRPC URL', 'Your Sift gRPC API URL'],
    ['Sift API REST URL', 'Your Sift REST API base URL'],
    ['Sift API Key', 'Your Sift API key'],
    ['Sample Rate (Hz)', 'The rate at which all Channels are streamed. Use a lower rate than the source data rate in FlexLogger; for full rate data, use the TDMS Uploader plugin instead.'],
    ['Select Channels', 'The FlexLogger Channels to stream to Sift'],
    ['Channel Name Prefix', 'A prefix applied to streamed channel names in Sift, to distinguish them from TDMS-uploaded channels.'],
   ]}
   />

## Upload a test file to Sift

1. In **FlexLogger**, configure the Channels you want to log.
2. Select **Add channels > Plug-in > Sift TDMS Uploader**.
3. Click the configure (gear) icon on the Sift TDMS Uploader plugin.
4. If you [set the environment variables](#set-environment-variables) beforehand, the fields are pre-filled. Otherwise, fill out the plugin settings.
   <MintTable
     columns={['Field', 'Description']}
     columnWidths={['25%', '75%']}
     rows={[
        ['Name', 'The display name for this plugin instance in FlexLogger'],
        ['Sift API gRPC URL', 'Your Sift gRPC API URL'],
        ['Sift API REST URL', 'Your Sift REST API base URL'],
        ['Sift API Key', 'Your Sift API key'],
        ['Asset Name', 'The name of the Sift Asset to upload data to'],
    ]}
   />
5. Start and stop a run in FlexLogger. Stopping the run generates a TDMS file for that session, which the plugin uploads to Sift automatically as a Run, as long as the plugin is configured in that project.
   <Info>
     **Run name**: To change the Run name, edit the TDMS file name in FlexLogger's Logging Specification tab.
   </Info>

## Verify

To verify data was received, open Sift and navigate to the Asset you configured. A new Run should appear containing the streamed or uploaded data.

If no data appears, check the plugin log files at `C:\Users\Public\Documents\National Instruments\FlexLogger\Plugins\IOPlugins\Sift Stream` for error details, and confirm that the API key, gRPC URL, and REST URL were entered correctly in the plugin settings.
