Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[logging] Add meta reporting #134

Open
wants to merge 1 commit into
base: yoann/prepare-internal-reporting
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ packages/tests/src/plugins/telemetry @DataDog/f
# Error Tracking
packages/plugins/error-tracking @yoannmoinet
packages/tests/src/plugins/error-tracking @yoannmoinet

# Analytics
packages/plugins/analytics @yoannmoinet
packages/tests/src/plugins/analytics @yoannmoinet
1 change: 1 addition & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export type GlobalContext = {
getLogger: GetLogger;
git?: RepositoryData;
pluginNames: string[];
sendLog: (message: string, ctx?: any) => Promise<void>;
start: number;
version: string;
};
Expand Down
8 changes: 8 additions & 0 deletions packages/factory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This is used to aggregate all the plugins and expose them to the bundler.

<!-- #toc -->
- [Internal Plugins](#internal-plugins)
- [Analytics](#analytics)
- [Build Report](#build-report)
- [Bundler Report](#bundler-report)
- [Git](#git)
Expand All @@ -23,6 +24,13 @@ These are the plugins that are used internally by the factory.
Most of the time they will interact via the global context.

<!-- #internal-plugins-list -->
### Analytics

> Send some analytics data to Datadog internally.

#### [📝 Full documentation ➡️](/packages/plugins/analytics#readme)


### Build Report

> This will populate `context.build` with a bunch of data coming from the build.
Expand Down
1 change: 1 addition & 0 deletions packages/factory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"dependencies": {
"@dd/core": "workspace:*",
"@dd/error-tracking-plugin": "workspace:*",
"@dd/internal-analytics-plugin": "workspace:*",
"@dd/internal-build-report-plugin": "workspace:*",
"@dd/internal-bundler-report-plugin": "workspace:*",
"@dd/internal-git-plugin": "workspace:*",
Expand Down
3 changes: 3 additions & 0 deletions packages/factory/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ export const getContext = ({
inject: () => {
throw new Error('Inject function called before it was initialized.');
},
sendLog: () => {
throw new Error('SendLog function called before it was initialized.');
},
start: Date.now(),
version,
};
Expand Down
2 changes: 2 additions & 0 deletions packages/factory/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type { OptionsWithErrorTracking } from '@dd/error-tracking-plugin/types';
import * as errorTracking from '@dd/error-tracking-plugin';
import type { OptionsWithTelemetry } from '@dd/telemetry-plugin/types';
import * as telemetry from '@dd/telemetry-plugin';
import { getAnalyticsPlugins } from '@dd/internal-analytics-plugin';
import { getBuildReportPlugins } from '@dd/internal-build-report-plugin';
import { getBundlerReportPlugins } from '@dd/internal-bundler-report-plugin';
import { getGitPlugins } from '@dd/internal-git-plugin';
Expand Down Expand Up @@ -81,6 +82,7 @@ export const buildPluginFactory = ({
const plugins: (PluginOptions | UnpluginOptions)[] = [
// Prefill with our internal plugins.
// #internal-plugins-injection-marker
...getAnalyticsPlugins(context),
...getBuildReportPlugins(context),
...getBundlerReportPlugins(context),
...getGitPlugins(options, context),
Expand Down
30 changes: 30 additions & 0 deletions packages/plugins/analytics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Analytics Plugin <!-- #omit in toc -->

Send some analytics data to Datadog internally.

It gives you acces to the `context.sendLog()` function.

```typescript
// Send a basic log.
context.sendLog('My basic log');

// Send some context with the log.
context.sendLog('My contextual log', { some: 'context' });
```

Every log already has some context to it:

```typescript
{
ddsource: string; // Name of the bundler plugin (e.g. `@datadog/webpack-plugin`).
env: Env; // Environment (e.g. `production`).
message; // The log message.
service: 'build-plugins';
bundler: {
name: string; // Name of the bundler (e.g. `webpack`).
version: string; // Version of the bundler.
};
plugins: PluginName[]; // List of the plugins/features enabled.
version: string; // Version of the plugin.
}
```
24 changes: 24 additions & 0 deletions packages/plugins/analytics/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@dd/internal-analytics-plugin",
"packageManager": "[email protected]",
"license": "MIT",
"private": true,
"author": "Datadog",
"description": "Send some analytics data to Datadog internally.",
"homepage": "https://github.com/DataDog/build-plugins/tree/main/packages/plugins/analytics#readme",
"repository": {
"type": "git",
"url": "https://github.com/DataDog/build-plugins",
"directory": "packages/plugins/analytics"
},
"exports": {
".": "./src/index.ts",
"./*": "./src/*.ts"
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@dd/core": "workspace:*"
}
}
9 changes: 9 additions & 0 deletions packages/plugins/analytics/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import type { PluginName } from '@dd/core/types';

export const PLUGIN_NAME: PluginName = 'datadog-analytics-plugin' as const;
export const INTAKE_PATH = 'v1/input/pub44d5f4eb86e1392037b7501f7adc540e';
export const INTAKE_HOST = 'browser-http-intake.logs.datadoghq.com';
69 changes: 69 additions & 0 deletions packages/plugins/analytics/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import { doRequest } from '@dd/core/helpers';
import type { GlobalContext, PluginOptions } from '@dd/core/types';

import { INTAKE_HOST, INTAKE_PATH, PLUGIN_NAME } from './constants';

export { PLUGIN_NAME } from './constants';

export const getAnalyticsPlugins = (context: GlobalContext): PluginOptions[] => {
const log = context.getLogger(PLUGIN_NAME);

context.sendLog = async (message: string, ctx: any = {}) => {
// Only send logs in production.
if (context.env !== 'production') {
return;
}

try {
const bundler = {
name: context.bundler.name,
version: context.bundler.version,
};

await doRequest({
// Don't delay the build too much on error.
retries: 2,
minTimeout: 100,
url: `https://${INTAKE_HOST}/${INTAKE_PATH}`,
method: 'POST',
type: 'json',
getData: async () => {
const data = {
ddsource: `@datadog/${bundler.name}-plugin`,
env: context.env,
message,
service: 'build-plugins',
bundler,
plugins: context.pluginNames,
version: context.version,
team: 'yoann',
...ctx,
};
return {
data: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
};
},
});
} catch (e: any) {
// We don't want to break anything in case of error.
log.debug(`Could not submit data to Datadog: ${e.message}`);
}
};

return [
{
name: PLUGIN_NAME,
async buildStart() {
// Send a log.
await context.sendLog('Build started');
},
},
];
};
10 changes: 10 additions & 0 deletions packages/plugins/analytics/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"rootDir": "./",
"outDir": "./dist"
},
"include": ["**/*"],
"exclude": ["dist", "node_modules"]
}
1 change: 1 addition & 0 deletions packages/tests/src/_jest/helpers/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export const getContextMock = (overrides: Partial<GlobalContext> = {}): GlobalCo
getLogger: jest.fn(),
inject: jest.fn(),
pluginNames: [],
sendLog: jest.fn(),
start: Date.now(),
version: 'FAKE_VERSION',
...overrides,
Expand Down
53 changes: 53 additions & 0 deletions packages/tests/src/unit/plugins/analytics/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import { INTAKE_HOST, INTAKE_PATH } from '@dd/internal-analytics-plugin/constants';
import { BUNDLERS, runBundlers } from '@dd/tests/_jest/helpers/runBundlers';
import nock from 'nock';

describe('Analytics Plugin', () => {
describe('Without network error.', () => {
test('Should submit the data.', async () => {
const replyMock = jest.fn(() => ({}));

nock(`https://${INTAKE_HOST}`)
// Intercept logs submissions.
.post(`/${INTAKE_PATH}`)
.times(BUNDLERS.length)
.reply(200, replyMock);

await runBundlers({
customPlugins: (options, context) => {
// Change the env so we DO send the logs.
context.env = 'production';
return [];
},
});

expect(replyMock).toHaveBeenCalledTimes(BUNDLERS.length);

nock.cleanAll();
});
});

describe('With a network error.', () => {
beforeAll(async () => {
nock(`https://${INTAKE_HOST}`)
// Intercept logs submissions.
.post(`/${INTAKE_PATH}`)
// Reply with an error.
.reply(500, 'Network error.')
.persist();
});

afterAll(async () => {
nock.cleanAll();
});

test('Should not throw.', async () => {
const { errors } = await runBundlers();
expect(errors).toHaveLength(0);
});
});
});
9 changes: 9 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,7 @@ __metadata:
dependencies:
"@dd/core": "workspace:*"
"@dd/error-tracking-plugin": "workspace:*"
"@dd/internal-analytics-plugin": "workspace:*"
"@dd/internal-build-report-plugin": "workspace:*"
"@dd/internal-bundler-report-plugin": "workspace:*"
"@dd/internal-git-plugin": "workspace:*"
Expand All @@ -1662,6 +1663,14 @@ __metadata:
languageName: unknown
linkType: soft

"@dd/internal-analytics-plugin@workspace:*, @dd/internal-analytics-plugin@workspace:packages/plugins/analytics":
version: 0.0.0-use.local
resolution: "@dd/internal-analytics-plugin@workspace:packages/plugins/analytics"
dependencies:
"@dd/core": "workspace:*"
languageName: unknown
linkType: soft

"@dd/internal-build-report-plugin@workspace:*, @dd/internal-build-report-plugin@workspace:packages/plugins/build-report":
version: 0.0.0-use.local
resolution: "@dd/internal-build-report-plugin@workspace:packages/plugins/build-report"
Expand Down