jmshal / bugsnag-js-cloudflare-workers

An un-official Bugsnag notifier for Cloudflare Workers.
MIT License
1 stars 3 forks source link

ReferenceError: navigator is not defined #1

Open shalzz opened 4 years ago

shalzz commented 4 years ago

Hi,

I get this error when using the library in a simple cloudflare worker project.

navigator = navigator || { userAgent: '' };
^

ReferenceError: navigator is not defined
jamesarosen commented 4 years ago

I got past this by changing that line to

self.navigator = self.navigator || { userAgent: '' };

But then I got

Uncaught TypeError: d is not a function at line 15065 in delivery

which is here, called from here.

I think that needs to change to

Bugsnag.delivery(() => delivery)

Though when I make that change, I get

Uncaught TypeError: logger.error is not a function

from here

simplenotezy commented 4 years ago

I have the same error; any help would be greatly appreciated in fixing this..

simplenotezy commented 4 years ago

@jmshal would be awesome to fix this

jamesarosen commented 4 years ago

Here's my working implementation. It's based off this project, but I've made some pretty substantial changes.

// Copyright 2020 Everlane, Inc.
// License: MIT
//
// Worker environment variables:
//  * `RELEASE_STAGE`: e.g. `"production"`, optional
//  * `BUGSNAG_API_KEY`: required for reporting, but will not error if unset

import Client from '@bugsnag/core/client'
import payload from '@bugsnag/core/lib/json-payload'
import { schema } from '@bugsnag/core/config'

const NULL_CLIENT = { notify: () => {} }

const notifier = {
  name: 'My Cloudflare Bugsnag Notifier',
  version: '0.1.0',
  url: 'https://github.com/my-company/cloudflare-bugsnare',
}

const app = {
  id: 'CDN',
  releaseStage: self.RELEASE_STAGE || '(unknown)',
  type: 'Service Worker',
}

function ipAddress(request) {
  return request.headers.get('cf-connecting-ip')
}

const HEADERS_TO_REDACT = ['authorization', 'cookie', 'x-csrf-token']

function parseRequest(request) {
  const url = new URL(request.url)

  const headers = Object.fromEntries([...request.headers.entries()])

  HEADERS_TO_REDACT.forEach(name => {
    if (headers[name]) {
      headers[name] = 'REDACTED'
    }
  })

  return {
    asn: request.cf.asn,
    clientIp: ipAddress(request),
    cloudflareRay: request.headers.get('cf-ray'),
    colo: request.cf.colo,
    headers,
    httpMethod: request.method,
    pathname: url.pathname,
    referer: request.headers.get('referer'),
    url: request.url,
  }
}

// This assumes Cloudflare Enterprise. For other plans, you'll just get null values for city, regionCode, …
function parseUser(request) {
  return {
    city: request.cf.city,
    continent: request.cf.continent,
    country: request.cf.country,
    id: ipAddress(request),
    regionCode: request.cf.regionCode,
    timezone: request.cf.timezone,
  }
}

function fetchDelivery(event) {
  return client => ({
    sendReport(report) {
      try {
        event.waitUntil(
          fetch(client.config.endpoints.notify, {
            body: payload.report(report, client.config.filters),
            headers: {
              'Content-Type': 'application/json',
              'Bugsnag-Api-Key': report.apiKey || client.config.apiKey,
              'Bugsnag-Payload-Version': '5',
              'Bugsnag-Sent-At': new Date().toISOString(),
            },
            method: 'POST',
          }),
        )
      } catch (e) {
        // ignore
      }
    },
  })
}

let bugsnag

export default function bugsnagClient(fetchEvent) {
  if (self.BUGSNAG_API_KEY == null) return NULL_CLIENT

  if (bugsnag != null) return bugsnag

  bugsnag = new Client(notifier)

  bugsnag.setOptions({
    apiKey: self.BUGSNAG_API_KEY,
  })

  bugsnag.delivery(fetchDelivery(fetchEvent))
  bugsnag.configure(schema)
  bugsnag.app = app
  bugsnag.request = parseRequest(fetchEvent.request)
  bugsnag.user = parseUser(fetchEvent.request)

  return bugsnag
}
simplenotezy commented 4 years ago

@jamesarosen that's cool, thanks for sharing.

Do you simply add this code in the top of your workers-site/index.js file, or how exactly do you include it?

jamesarosen commented 4 years ago

Here's how we use it:

addEventListener('fetch', event => {
  const bugsnag = bugsnagClient(event)
  try {
    event.respondWith(handle(event.request))
  } catch (e) {
    const handledState = {
      severity: 'error',
      unhandled: true,
      severityReason: {
        type: 'unhandledException',
        attributes: { framework: 'Cloudflare Workers' },
      },
    }
    const report = createReportFromError(e, handledState)
    report.groupingHash = e.message || 'Uncaught exception'
    bugsnagClient(event).notify(report)
    event.respondWIth(new Response(message, { status: 500 }))
  }
})