First, create a new project using a tool such as **electron-vite**. You can do this by running the command npm create @quick-start/electron@latest. If you're familiar with the Vite ecosystem, you'll find this straightforward. If not, simply answer all questions with 'Yes'. Let's proceed...
Next, you need to install Open Telemetry Dependencies.
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.47.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.52.0",
"@opentelemetry/instrumentation": "^0.52.0",
"@opentelemetry/resources": "^1.25.0",
"@opentelemetry/semantic-conventions": "^1.25.0",
Now, create a .env file to establish two values in your variables.
VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://otlp.nr-data.net:4318/v1/traces
VITE_OTEL_EXPORTER_OTLP_API_KEY="your-ingest-license-new-relic-api-key"
Now, create a folder named instrumentations. Inside it, create a file named registerAppInstrumentations.js and follow the code below:
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { Resource } from '@opentelemetry/resources'
import {
SEMRESATTRS_SERVICE_NAME,
SEMRESATTRS_SERVICE_VERSION,
SEMRESATTRS_OS_VERSION
} from '@opentelemetry/semantic-conventions'
import { NodeTracerProvider, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
export const registerAppInstrumentations = (appVersion: string, operatingSystem: string, appName: string): void => {
registerInstrumentations({
instrumentations: [
getNodeAutoInstrumentations()
]
})
const resource = Resource.default().merge(
new Resource({
[SEMRESATTRS_SERVICE_NAME]: NEWRELIC_APP_NAME,
[SEMRESATTRS_SERVICE_VERSION]: appVersion,
[SEMRESATTRS_OS_VERSION]: operatingSystem
})
)
const provider = new NodeTracerProvider({
resource: resource
})
const otlpTraceExporter = new OTLPTraceExporter({
url: import.meta.env.VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
headers: {
"api-key": import.meta.env.VITE_OTEL_EXPORTER_OTLP_API_KEY,
},
});
const processor = import.meta.env.DEV
? new SimpleSpanProcessor(otlpTraceExporter)
: new BatchSpanProcessor(otlpTraceExporter)
provider.addSpanProcessor(processor)
provider.register()
}
Now, let's incorporate the functions we created into the main process of the Electron app. Follow the code below:
import { app, shell, BrowserWindow, ipcMain } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import { trace, SpanStatusCode } from '@opentelemetry/api'
import icon from '../../resources/icon.png?asset'
import { name } from '../../package.json'
import { registerAppInstrumentations } from '../instrumentations/registerAppInstrumentations'
app.whenReady().then(() => {
const appVersion = app.getVersion()
const operatingSystem = process.platform;
const appName = name;
registerAppInstrumentations(appVersion, operatingSystem, appName)
electronApp.setAppUserModelId('com.electron')
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
ipcMain.on('ping', async () => {
await trace.getTracer("ping").startActiveSpan('fetchPingPong', async (span) => {
try {
span.setAttribute('ping', 'pong')
await new Promise((resolve) => setTimeout(resolve, 1000))
span.addEvent('ping', { pong: 'ping' })
} catch (error: any) {
span.recordException(error)
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message })
console.error(error)
} finally {
span.end()
}
})
})
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
You can see the full code on my GitHub.