Issue with custom plugin for Grafana on the server

Hi! I am working on developing a datepicker plugin, and I have encountered a problem. Everything works correctly in the local environment, but on the server, part of the functionality is not executing. For example, no console.log statements are outputting on the server, even though everything works locally.
Here is the component code:

import React, { useState, useEffect } from 'react';
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from 'types';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
import { Box } from '@mui/material';
import moment, { Moment } from 'moment';
import { useHistory } from 'react-router-dom';
import StyledDatePicker from './StyledDatePicker';
import { IconButton, Tooltip } from '@mui/material';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';

interface Props extends PanelProps<SimpleOptions> {}

export const DateRangePicker: React.FC<Props> = ({ timeRange, onChangeTimeRange, options, height }) => {
  console.log('DateRangePicker rendered');

  const history = useHistory();
  const { timeRangeData } = options;

  const [startDate, setStartDate] = useState<Moment | null>(moment(timeRange.from.toISOString()));
  const [endDate, setEndDate] = useState<Moment | null>(moment(timeRange.to.toISOString()));
  const [applyClicked, setApplyClicked] = useState<boolean>(false);
  const [isChangeDate, setIsChangeDate] = useState<boolean>(false);

  const DATE_CONSTANT = {
    '5m': 'now-5m',
    '15m': 'now-15m',
    '30m': 'now-30m',
    '1h': 'now-1h',
    '3h': 'now-3h',
    '6h': 'now-6h',
    '12h': 'now-12h',
    '24h': 'now-24h',
    '2d': 'now-2d',
    '7d': 'now-7d',
    '30d': 'now-30d',
    '90d': 'now-90d',
    '6M': 'now-6M',
    '1y': 'now-1y',
  };

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const toParam = params.get('to');
    if (!applyClicked && toParam === 'now' && !isChangeDate) {
      setStartDate(moment(timeRange.from.toISOString()));
      setEndDate(moment(timeRange.to.toISOString()));
    }
  }, [timeRange, applyClicked]);

  const applyDateRange = () => {
    if (startDate && endDate) {
      onChangeTimeRange({
        from: moment(startDate).valueOf(),
        to: moment(endDate).valueOf(),
      });
    }
    setApplyClicked(true);
  };

  const resetDateTime = () => {
    const currentUrl = new URL(window.location.href);
    const params = new URLSearchParams(currentUrl.search);
    params.set('from', DATE_CONSTANT[timeRangeData as keyof typeof DATE_CONSTANT]);
    params.set('to', 'now');
    const newUrl = `${currentUrl.pathname}?${params.toString()}`;
    history.push(newUrl);
    setApplyClicked(false);
    setIsChangeDate(false);
  };

  return (
    <LocalizationProvider dateAdapter={AdapterMoment}>
      <Box display="flex" flexDirection="column" gap={2}>
        <Box display="flex" width="100%" gap={2}>
          <Box flex="1">
            <StyledDatePicker
              height={height}
              label={'Start date'}
              value={startDate}
              onChange={(newDate) => {
                setStartDate(newDate);
                setIsChangeDate(true);
              }}
              maxDateTime={endDate || undefined}
              onOpen={() => setApplyClicked(true)}
              onClose={() => setApplyClicked(false)}
            />
          </Box>
          <Box flex="1">
            <StyledDatePicker
              height={height}
              label={'End Date'}
              value={endDate}
              onChange={(newDate) => {
                setEndDate(newDate);
                setIsChangeDate(true);
              }}
              minDateTime={startDate || undefined}
              onOpen={() => setApplyClicked(true)}
              onClose={() => setApplyClicked(false)}
            />
          </Box>
          <Box
            display="flex"
            flexDirection="row"
            alignItems="center"
            justifyContent="space-between"
            sx={{ pt: 0, mt: 0 }}
          >
            <Tooltip title={'Query'}>
              <IconButton onClick={applyDateRange}>
                <CheckIcon sx={{ color: 'green' }} />
              </IconButton>
            </Tooltip>
            <Tooltip title={'Reset'}>
              <IconButton onClick={resetDateTime}>
                <CloseIcon sx={{ color: 'red' }} />
              </IconButton>
            </Tooltip>
          </Box>
        </Box>
      </Box>
    </LocalizationProvider>
  );
};

Here are the steps I take to deploy the code on the server:

  1. I build the project.
  2. I sign the plugin.
  3. I copy the dist folder into the Grafana plugins directory on the server.
  4. I restart the Grafana service.

Could you please advise me on what the issue might be and how to resolve it?

Grafana doesn’t execute any javascript in the server-side. All the server-side (backend) of Grafana is written in go.

Do you maybe mean you can’t see your console.log statements in the browser when you test it in a Grafana instance different from your development one?

The default webpack configuration that comes with create-plugin drops console.log statements that might explain why you don’t see console.logs in your browser if you built your plugin in production mode,.

Is the plugin functionality working correctly? (other than console.logs calls not appearing)
Is your custom plugin correctly listed in the installed plugins?

The code is running on the server, meaning the project is deployed on the server. The plugin itself displays in the server environment, and I can use it, but for some reason, this useEffect doesn’t work in the server environment, although it works correctly locally.

useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const toParam = params.get('to');
    if (!applyClicked && toParam === 'now' && !isChangeDate) {
      setStartDate(moment(timeRange.from.toISOString()));
      setEndDate(moment(timeRange.to.toISOString()));
    }
  }, [timeRange, applyClicked]);

That’s the issue.

@maxymzakharkiv We published an article recently from a community member who implemented custom Date Picker using existing community plugin without developing a new one.

Check it out:

This is a bit different from what I need. I’ve already developed the plugin, and it works locally, but for some reason, part of the functionality isn’t working in the server environment (as I described above). I’m not sure what the issue is here

Curious, Why is there a need to use console log on server after plugin is deployed?

Wonder if the plugin build process strips out those kinds of things

I’m deploying this on a test server and I needed to debug the code this way because again, the plugin works correctly locally, but on the test server there is no console.log output and the useEffect from the code I provided above access is not working. I can’t figure out why this is happening.

Is it not just as advised above?

That:
The default webpack configuration that comes with create-plugin drops console.log statements

1 Like

Well as @davidharris pointed out you can’t so maybe go the write to log route?

For some reason, the timeRange on the remote server isn’t working for auto-refreshing the date, but it works locally. Why is this happening?

I made it so that console.log messages now appear, but it didn’t solve the problem. Locally, useEffect triggers based on the interval (set to 5 seconds - it works every 5 seconds), but in the server environment, useEffect only fires once. This needs to be fixed.

Is it possible your production environment is significantly different from your development environment?

What version are you running locally vs in your production server?

There are some missing pieces of this code to understand why it would not run. the best would be that you share all your plugin code and details of your development and production environments to find what could be happening.

So far no other errors had been reported about time picker management in Grafana like this. I suspect this might be more related to the plugin not working well in different Grafana versions than Grafana itself

The issue in the code is only with this useEffect, everything else works correctly. I found that the problem lies in the timeRange parameter from Grafana; locally, it updates correctly, but on the server, it only updates once. Locally in package.json, I have:

"@grafana/data": "^11.3.0",
"@grafana/runtime": "^11.3.0",
"@grafana/schema": "^11.3.0",
"@grafana/ui": "^11.3.0"

while on the server, it’s "version": "11.2.0", which I verified via http://your-server-ip:3000/api/health.

The issue has been resolved