Skip to content

Commit

Permalink
feat(node): Add app.free_memory info to events (#12150)
Browse files Browse the repository at this point in the history
  • Loading branch information
AbhiPrasad authored May 21, 2024
1 parent f3c8a86 commit 4420844
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
31 changes: 29 additions & 2 deletions packages/node/src/integrations/context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import { execFile } from 'node:child_process';
import { readFile, readdir } from 'node:fs';
import * as os from 'node:os';
Expand All @@ -18,6 +19,12 @@ import type {
export const readFileAsync = promisify(readFile);
export const readDirAsync = promisify(readdir);

// Process enhanced with methods from Node 18, 20, 22 as @types/node
// is on `14.18.0` to match minimum version requirements of the SDK
interface ProcessWithCurrentValues extends NodeJS.Process {
availableMemory?(): number;
}

const INTEGRATION_NAME = 'Context';

interface DeviceContextOptions {
Expand Down Expand Up @@ -114,10 +121,18 @@ export const nodeContextIntegration = defineIntegration(_nodeContextIntegration)
*/
function _updateContext(contexts: Contexts): Contexts {
// Only update properties if they exist

if (contexts?.app?.app_memory) {
contexts.app.app_memory = process.memoryUsage().rss;
}

if (contexts?.app?.free_memory && typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {
const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();
if (freeMemory != null) {
contexts.app.free_memory = freeMemory;
}
}

if (contexts?.device?.free_memory) {
contexts.device.free_memory = os.freemem();
}
Expand Down Expand Up @@ -183,11 +198,23 @@ function getCultureContext(): CultureContext | undefined {
return;
}

function getAppContext(): AppContext {
/**
* Get app context information from process
*/
export function getAppContext(): AppContext {
const app_memory = process.memoryUsage().rss;
const app_start_time = new Date(Date.now() - process.uptime() * 1000).toISOString();
// https://nodejs.org/api/process.html#processavailablememory
const appContext: AppContext = { app_start_time, app_memory };

if (typeof (process as ProcessWithCurrentValues).availableMemory === 'function') {
const freeMemory = (process as ProcessWithCurrentValues).availableMemory?.();
if (freeMemory != null) {
appContext.free_memory = freeMemory;
}
}

return { app_start_time, app_memory };
return appContext;
}

/**
Expand Down
19 changes: 19 additions & 0 deletions packages/node/test/helpers/conditional.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { parseSemver } from '@sentry/utils';

const NODE_VERSION = parseSemver(process.versions.node).major;

/**
* Returns`describe` or `describe.skip` depending on allowed major versions of Node.
*
* @param {{ min?: number; max?: number }} allowedVersion
* @return {*} {jest.Describe}
*/
export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.It => {
if (!NODE_VERSION) {
return it.skip;
}

return NODE_VERSION < (allowedVersion.min || -Infinity) || NODE_VERSION > (allowedVersion.max || Infinity)
? test.skip
: test;
};
30 changes: 28 additions & 2 deletions packages/node/test/integrations/context.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
import * as os from 'os';
import * as os from 'node:os';

import { getDeviceContext } from '../../src/integrations/context';
import { getAppContext, getDeviceContext } from '../../src/integrations/context';
import { conditionalTest } from '../helpers/conditional';

describe('Context', () => {
describe('getAppContext', () => {
afterAll(() => {
jest.clearAllMocks();
});

conditionalTest({ max: 18 })('it does not return free_memory on older node versions', () => {
const appContext = getAppContext();
expect(appContext.free_memory).toBeUndefined();
});

conditionalTest({ min: 22 })(
'returns free_memory if process.availableMemory is defined and returns a valid value',
() => {
const appContext = getAppContext();
expect(appContext.free_memory).toEqual(expect.any(Number));
},
);

conditionalTest({ min: 22 })('returns no free_memory if process.availableMemory ', () => {
jest.spyOn(process as any, 'availableMemory').mockReturnValue(undefined as unknown as number);
const appContext = getAppContext();
expect(appContext.free_memory).toBeUndefined();
});
});

describe('getDeviceContext', () => {
afterAll(() => {
jest.clearAllMocks();
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface AppContext extends Record<string, unknown> {
app_identifier?: string;
build_type?: string;
app_memory?: number;
free_memory?: number;
}

export interface DeviceContext extends Record<string, unknown> {
Expand Down

0 comments on commit 4420844

Please sign in to comment.