getsentry / sentry-electron

The official Sentry SDK for Electron
https://sentry.io/
MIT License
219 stars 56 forks source link

Can't resolve `opentelemetry-instrumentation-fetch-node` #932

Open stoefln opened 6 days ago

stoefln commented 6 days ago

Upgrading from @sentry/electron v4 to v5 is not possible for me.

Environment

SaaS (https://sentry.io/)

Steps to Reproduce

  1. install electron 12.2.3
  2. use webpack 5 as bundler
  3. install @sentry/electron@5.1.0
  4. launch webpack-dev-server -> Errors are thrown in main and renderer threads (see below)

Expected Result

No error is thrown

Actual Result

-> Error is thrown in renderer thread:

WARNING in ./node_modules/@sentry/node/esm/integrations/node-fetch.js 20:24-74
Module not found: Error: Can't resolve 'opentelemetry-instrumentation-fetch-node' in '/Users/me/Workspace/myapp/node_modules/@sentry/node/esm/integrations'
 @ ./node_modules/@sentry/node/esm/index.js 2:0-74 2:0-74
 @ ./app/lib/ErrorReporting.js 281:16-39
 @ ./app/main.js 14:0-82 24:0-26 25:0-11

-> Error is thrown in main thread:

App threw an error during load
Error: Cannot find module 'node:util'
Require stack:
- /Users/me/Workspace/myapp/node_modules/@sentry/node/cjs/integrations/console.js
- /Users/me/Workspace/myapp/node_modules/@sentry/node/cjs/index.js
- /Users/me/Workspace/myapp/node_modules/@sentry/electron/main/index.js
- /Users/me/Workspace/myapp/static/index-bundle.js
- /Users/me/Workspace/myapp/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar/main.js
-
    at Module._resolveFilename (internal/modules/cjs/loader.js:887:15)
    at Function.n._resolveFilename (electron/js2c/browser_init.js:261:1128)
    at Module._load (internal/modules/cjs/loader.js:732:27)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12913)
    at Module.require (internal/modules/cjs/loader.js:959:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (/Users/me/Workspace/myapp/node_modules/@sentry/node/cjs/integrations/console.js:3:14)
    at Module._compile (internal/modules/cjs/loader.js:1078:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1108:10)
    at Module.load (internal/modules/cjs/loader.js:935:32)

Product Area

Unknown

Link

https://stephan-petzl.sentry.io/

DSN

https://stephan-petzl.sentry.io/issues/?project=1554144

Version

No response

getsantry[bot] commented 6 days ago

Assigning to @getsentry/support for routing ⏲️

getsantry[bot] commented 6 days ago

Routing to @getsentry/product-owners-issues for triage ⏲️

timfish commented 6 days ago

Which version of Electron are you using?

Have you followed the migration docs around importing the SDK to init it in each process?

stoefln commented 5 days ago

@timfish electron 12.2.3 and yes, i have imported sentry just as in the docs

timfish commented 5 days ago

electron 12.2.3

Ah that is likely the issue.

For v5 of the SDK, the minimum Electron version has to be increased to v15 due to the required Node version.

Out of interest, what's blocking your update to a newer Electron version? Personally, I've got an app stuck on v18 because we need to migrate away from ffi-napi.

stoefln commented 5 days ago

After upgrade to electron 16 I am still getting this error:

Module not found: Error: Can't resolve 'opentelemetry-instrumentation-fetch-node' in '/Users/me/Workspace/myapp/node_modules/@sentry/node/cjs/integrations'
 @ ./node_modules/@sentry/node/cjs/index.js 4:18-57
 @ ./app/lib/ErrorReporting.js 281:16-39
 @ ./app/main.js 14:0-82 24:0-26 25:0-11
timfish commented 5 days ago

opentelemetry-instrumentation-fetch-node is an optional dependency of @sentry/node which is loaded asynchronously if it has been added as a dependency.

Can you share more of your Sentry initialisation code and webpack config? We don't see this error in our webpack integration tests!

stoefln commented 2 days ago

index.js (main process):

import * as Sentry from "@sentry/electron/main";
Sentry.init(getMainConfig(app.getVersion()))

function getMainConfig(version) {
  const Sentry = require('@sentry/electron/main')

  const config = getConfig(version)
  const mainConfig = {
    ...config,
    attachScreenshot: true
  }
  return mainConfig
}

function getConfig(version) {
  return {
    // changed for 1.4. and up. Changing this dsn from major release to major release will allow us to switch off error reporting for older versions.
    // new DSNs can be created here: https://...sentry.io/settings/projects/.../keys/
    dsn: 'https://6bf63d16f4d24...',
    release: 'Repeato-' + version,
    debug: isDev,

    beforeBreadcrumb(breadcrumb) {
      // remove additional data if only 1 argument was passed to console - avoid redundant data
      if (breadcrumb.category === 'console' && breadcrumb.data?.arguments?.length === 1) delete breadcrumb.data
      return breadcrumb
    }
  }
}

app.js (renderer):

import * as Sentry from "@sentry/electron/renderer"
initRendererErrorReporting(Sentry).catch(console.error)

async function initRendererErrorReporting(_sentry) {
  const electron = await import('electron')
  const version = electron.remote.app.getVersion()
  console.log('initRendererErrorReporting', _sentry)
  Sentry = _sentry
  if (!isDev) {
    Sentry.init(getRendererConfig(version))
  }

  const logError = log.error
  log.error = function () {
    var args = Array.prototype.slice.call(arguments)
    const error = reduceConsoleArgs(args)
    handleError(Sentry, error)
    return logError.apply(log, args)
  }

  var consoleError = console.error
  console.error = function () {
    var args = Array.prototype.slice.call(arguments)
    const error = reduceConsoleArgs(args)
    handleError(Sentry, error)
    return consoleError.apply(console, args)
  }
}

function getRendererConfig(version) {
  const config = getConfig(version)

  const integrations = []

  const rendererConfig = {
    ...config,

    // This sets the sample rate to be 10%. You may want this to be 100% while
    // in development and sample at a lower rate in production
    replaysSessionSampleRate: 0.1,

    // If the entire session is not sampled, use the below sample rate to sample
    // sessions when an error occurs.
    replaysOnErrorSampleRate: 0.1,

    integrations

  }
  return rendererConfig
}

webpack.entrypoint.config.mjs (for main process):

import path from 'path'
import webpack from 'webpack'
import nodeExternals from 'webpack-node-externals'
import __dirname from './app/utils/dirname.mjs'

export default {
  devtool: 'source-map',
  entry: ['@babel/polyfill', './index.js'],
  mode: 'development',
  output: {
    filename: 'index-bundle.js',
    path: path.resolve(__dirname, 'static')
    //clean: true
  },
  module: {
    noParse: /iconv-loader\.js/,
    rules: [
      {
        test: /\.node$/,
        use: 'node-loader'
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/, 
        use: ['babel-loader']
      }
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development')
    }),
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'development',
      DEBUG_PROD: false,
      START_MINIMIZED: false,
      E2E_BUILD: false
    })
  ],
  target: 'electron12.2-main',
  resolve: {
    fallback: {
      // Provide polyfills or mocks for node modules used in the browser if necessary
    }
  },
  externals: [
    nodeExternals({
      allowlist: /\.css$/
    })
  ],
  experiments: {
    // Future Webpack features can be enabled here
  },
  infrastructureLogging: {
    // This can help in debugging and logging module resolution
    level: 'info'
  }
}

webpack.config.js (for renderer process)

import { existsSync } from 'fs'
import path from 'path'
import webpack from 'webpack'
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'
import __dirname from './app/utils/dirname.mjs'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
const buildWorker = existsSync('./app/worker/worker.js')
console.log(`Build worker:`, buildWorker)
//, new BundleAnalyzerPlugin()
const plugins = [new ReactRefreshWebpackPlugin()]
if (!buildWorker) {
  plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /TemplateMatcherWorker/ }))
  plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /OcrWorker/ }))
  plugins.push(new webpack.IgnorePlugin({ resourceRegExp: /ObjectFinder$/ }))
}

const devServerFilePath = path.join(__dirname, 'static')
console.log(`Dev server file path: ${devServerFilePath}`)
/**
 * @type {import('webpack').Configuration}
 */
const config = {
  devtool: 'eval-cheap-module-source-map',
  entry: {
    main: ['@babel/polyfill', './app/main.js'],
    pdfexport: ['@babel/polyfill', './app/pdfExport.js'],
    loading: ['@babel/polyfill', './app/loading.js'],
    ...(buildWorker && { worker: ['@babel/polyfill', './app/worker/worker.js'] })
  },
  mode: 'development',
  output: {
    filename: '[name]-bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },

  module: {
    rules: [
      {
        test: /\.node$/,
        use: 'node-loader'
      },
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: 'ts-loader'
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)\/(?!(aws-sdk|@aws-sdk|@smithy)\/).*/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /\.mjs$/,
        include: /node_modules/,
        type: 'javascript/auto'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(asset)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name]'
            }
          }
        ]
      }
    ]
  },

  plugins,
  externals: {
    'monaco-editor': 'monaco-editor'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx']
  },

  devServer: {
    host: 'localhost',
    port: process.env.PORT || 3000,
    historyApiFallback: true,
    hot: true,
    static: {
      directory: devServerFilePath
    }
  },
  target: 'electron12.2-renderer',

  watchOptions: {
    poll: 1000,
    ignored: ['node_modules/**'],
    aggregateTimeout: 600
  }
}

export default config
timfish commented 1 day ago

Since the issue is only with the main process, I've only looked at those relevant files.

In our tests we use target: "electron-main" but we have a much more basic config. electron12.2-main might be causing issues? Also, since this in the main process, with the correct target, you should not need to include anything for externals, so nodeExternals({allowlist: /\.css$/}) seems unnecessary here.

Which version of webpack are you using? If you can point me to a repository that reproduces the issue I can debug this and probably give more guidance.