Chore: Switch Grafana to using faro libraries (#58186)

This commit is contained in:
Timur Olzhabayev
2022-11-08 10:36:27 +01:00
committed by GitHub
parent 82d09e0647
commit 228ec4c0f3
8 changed files with 184 additions and 117 deletions

View File

@@ -1,6 +1,8 @@
import { BaseTransport, TransportItem } from '@grafana/agent-core';
import { getEchoSrv, EchoEventType } from '@grafana/runtime';
import { BaseTransport, TransportItem } from '@grafana/faro-core';
import { getEchoSrv, EchoEventType, config } from '@grafana/runtime';
export class EchoSrvTransport extends BaseTransport {
readonly name: string = 'EchoSrvTransport';
readonly version: string = config.buildInfo.version;
send(event: TransportItem) {
getEchoSrv().addEvent({
type: EchoEventType.GrafanaJavascriptAgent,

View File

@@ -1,18 +1,18 @@
import { BaseTransport } from '@grafana/agent-core';
import { FetchTransport, initializeAgent } from '@grafana/agent-web';
import { BuildInfo } from '@grafana/data';
import { GrafanaEdition } from '@grafana/data/src/types/config';
import { BaseTransport, Instrumentation, InternalLoggerLevel } from '@grafana/faro-core';
import { FetchTransport, initializeFaro } from '@grafana/faro-web-sdk';
import { EchoEventType, EchoMeta } from '@grafana/runtime';
import { GrafanaJavascriptAgentBackend, GrafanaJavascriptAgentBackendOptions } from './GrafanaJavascriptAgentBackend';
import { GrafanaJavascriptAgentEchoEvent } from './types';
jest.mock('@grafana/agent-web', () => {
const originalModule = jest.requireActual('@grafana/agent-web');
jest.mock('@grafana/faro-web-sdk', () => {
const originalModule = jest.requireActual('@grafana/faro-web-sdk');
return {
__esModule: true,
...originalModule,
initializeAgent: jest.fn(),
initializeFaro: jest.fn(),
};
});
@@ -52,8 +52,8 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
it('will set up FetchTransport if customEndpoint is provided', async () => {
// arrange
const originalModule = jest.requireActual('@grafana/agent-web');
jest.mocked(initializeAgent).mockImplementation(originalModule.initializeAgent);
const originalModule = jest.requireActual('@grafana/faro-web-sdk');
jest.mocked(initializeFaro).mockImplementation(originalModule.initializeFaro);
//act
const backend = new GrafanaJavascriptAgentBackend(options);
@@ -66,6 +66,19 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
it('will initialize GrafanaJavascriptAgent and set user', async () => {
// arrange
const mockedSetUser = jest.fn();
const mockedInstrumentationsForConfig: Instrumentation[] = [];
const mockedInstrumentations = {
add: jest.fn(),
instrumentations: mockedInstrumentationsForConfig,
remove: jest.fn(),
};
const mockedInternalLogger = {
prefix: 'Faro',
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
const mockedAgent = () => {
return {
api: {
@@ -75,24 +88,38 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
pushError: jest.fn(),
pushMeasurement: jest.fn(),
pushTraces: jest.fn(),
pushEvent: jest.fn(),
initOTEL: jest.fn(),
getOTEL: jest.fn(),
getTraceContext: jest.fn(),
changeStacktraceParser: jest.fn(),
getStacktraceParser: jest.fn(),
isOTELInitialized: jest.fn(),
setSession: jest.fn(),
getSession: jest.fn(),
resetUser: jest.fn(),
resetSession: jest.fn(),
},
config: {
globalObjectKey: '',
instrumentations: [],
preventGlobalExposure: false,
transports: [],
instrumentations: mockedInstrumentationsForConfig,
metas: [],
parseStacktrace: jest.fn(),
app: jest.fn(),
paused: false,
dedupe: true,
isolate: false,
internalLoggerLevel: InternalLoggerLevel.ERROR,
unpatchedConsole: { ...console },
},
metas: {
add: jest.fn(),
remove: jest.fn(),
value: {},
addListener: jest.fn(),
removeListener: jest.fn(),
},
transports: {
add: jest.fn(),
@@ -100,18 +127,27 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
transports: [],
pause: jest.fn(),
unpause: jest.fn(),
addBeforeSendHooks: jest.fn(),
addIgnoreErrorsPatterns: jest.fn(),
getBeforeSendHooks: jest.fn(),
isPaused: jest.fn(),
remove: jest.fn(),
removeBeforeSendHooks: jest.fn(),
},
pause: jest.fn(),
unpause: jest.fn(),
instrumentations: mockedInstrumentations,
internalLogger: mockedInternalLogger,
unpatchedConsole: { ...console },
};
};
jest.mocked(initializeAgent).mockImplementation(mockedAgent);
jest.mocked(initializeFaro).mockImplementation(mockedAgent);
//act
new GrafanaJavascriptAgentBackend(options);
//assert
expect(initializeAgent).toHaveBeenCalledTimes(1);
expect(initializeFaro).toHaveBeenCalledTimes(1);
expect(mockedSetUser).toHaveBeenCalledTimes(1);
expect(mockedSetUser).toHaveBeenCalledWith({
id: '504',
@@ -124,6 +160,19 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
it('will forward events to transports', async () => {
//arrange
const mockedSetUser = jest.fn();
const mockedInstrumentationsForConfig: Instrumentation[] = [];
const mockedInstrumentations = {
add: jest.fn(),
instrumentations: mockedInstrumentationsForConfig,
remove: jest.fn(),
};
const mockedInternalLogger = {
prefix: 'Faro',
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
};
const mockedAgent = () => {
return {
api: {
@@ -133,24 +182,38 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
pushError: jest.fn(),
pushMeasurement: jest.fn(),
pushTraces: jest.fn(),
pushEvent: jest.fn(),
initOTEL: jest.fn(),
getOTEL: jest.fn(),
getTraceContext: jest.fn(),
changeStacktraceParser: jest.fn(),
getStacktraceParser: jest.fn(),
isOTELInitialized: jest.fn(),
setSession: jest.fn(),
getSession: jest.fn(),
resetUser: jest.fn(),
resetSession: jest.fn(),
},
config: {
globalObjectKey: '',
instrumentations: [],
preventGlobalExposure: false,
transports: [],
instrumentations: mockedInstrumentationsForConfig,
metas: [],
parseStacktrace: jest.fn(),
app: jest.fn(),
paused: false,
dedupe: true,
isolate: false,
internalLoggerLevel: InternalLoggerLevel.ERROR,
unpatchedConsole: { ...console },
},
metas: {
add: jest.fn(),
remove: jest.fn(),
value: {},
addListener: jest.fn(),
removeListener: jest.fn(),
},
transports: {
add: jest.fn(),
@@ -158,13 +221,22 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
transports: [],
pause: jest.fn(),
unpause: jest.fn(),
addBeforeSendHooks: jest.fn(),
addIgnoreErrorsPatterns: jest.fn(),
getBeforeSendHooks: jest.fn(),
isPaused: jest.fn(),
remove: jest.fn(),
removeBeforeSendHooks: jest.fn(),
},
pause: jest.fn(),
unpause: jest.fn(),
instrumentations: mockedInstrumentations,
internalLogger: mockedInternalLogger,
unpatchedConsole: { ...console },
};
};
jest.mocked(initializeAgent).mockImplementation(mockedAgent);
jest.mocked(initializeFaro).mockImplementation(mockedAgent);
const backend = new GrafanaJavascriptAgentBackend({
...options,
preventGlobalExposure: true,
@@ -195,8 +267,8 @@ describe('GrafanaJavascriptAgentEchoBackend', () => {
// // use actual GrafanaJavascriptAgent & mock window.fetch
// // arrange
// const originalModule = jest.requireActual('@grafana/agent-web');
// jest.mocked(initializeAgent).mockImplementation(originalModule.initializeAgent);
// const originalModule = jest.requireActual('@grafana/faro-web-sdk');
// jest.mocked(initializeFaro).mockImplementation(originalModule.initializeFaro);
// const fetchSpy = (window.fetch = jest.fn());
// fetchSpy.mockResolvedValue({ status: 200 } as Response);
// const echo = new Echo({ debug: true });

View File

@@ -1,14 +1,14 @@
import { BaseTransport } from '@grafana/agent-core';
import { BuildInfo } from '@grafana/data';
import { BaseTransport } from '@grafana/faro-core';
import {
initializeAgent,
initializeFaro,
defaultMetas,
BrowserConfig,
ErrorsInstrumentation,
ConsoleInstrumentation,
WebVitalsInstrumentation,
FetchTransport,
} from '@grafana/agent-web';
import { BuildInfo } from '@grafana/data';
} from '@grafana/faro-web-sdk';
import { EchoBackend, EchoEvent, EchoEventType } from '@grafana/runtime';
import { EchoSrvTransport } from './EchoSrvTransport';
@@ -27,7 +27,7 @@ export class GrafanaJavascriptAgentBackend
implements EchoBackend<GrafanaJavascriptAgentEchoEvent, GrafanaJavascriptAgentBackendOptions>
{
supportedEvents = [EchoEventType.GrafanaJavascriptAgent];
private agentInstance;
private faroInstance;
transports: BaseTransport[];
constructor(public options: GrafanaJavascriptAgentBackendOptions) {
@@ -51,7 +51,7 @@ export class GrafanaJavascriptAgentBackend
// initialize GrafanaJavascriptAgent so it can set up its hooks and start collecting errors
const grafanaJavaScriptAgentOptions: BrowserConfig = {
globalObjectKey: options.globalObjectKey || 'grafanaAgent',
globalObjectKey: options.globalObjectKey || 'faro',
preventGlobalExposure: options.preventGlobalExposure || false,
app: {
version: options.buildInfo.version,
@@ -74,10 +74,10 @@ export class GrafanaJavascriptAgentBackend
},
],
};
this.agentInstance = initializeAgent(grafanaJavaScriptAgentOptions);
this.faroInstance = initializeFaro(grafanaJavaScriptAgentOptions);
if (options.user) {
this.agentInstance.api.setUser({
this.faroInstance.api.setUser({
id: options.user.id,
attributes: {
orgId: String(options.user.orgId) || '',

View File

@@ -1,4 +1,4 @@
import { agent, LogLevel as GrafanaLogLevel } from '@grafana/agent-web';
import { faro, LogLevel as GrafanaLogLevel } from '@grafana/faro-web-sdk';
import { config } from '@grafana/runtime/src';
export const LogMessages = {
@@ -15,7 +15,7 @@ export const LogMessages = {
// logInfo from '@grafana/runtime' should be used, but it doesn't handle Grafana JS Agent and Sentry correctly
export function logInfo(message: string, context: Record<string, string | number> = {}) {
if (config.grafanaJavascriptAgent.enabled) {
agent.api.pushLog([message], {
faro.api.pushLog([message], {
level: GrafanaLogLevel.INFO,
context: { ...context, module: 'Alerting' },
});