Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@charmverse/core",
"version": "0.126.3",
"version": "0.126.4-rc-datadog-logs.1",
"description": "Core API for Charmverse",
"type": "commonjs",
"types": "./dist/cjs/index.d.ts",
Expand Down
52 changes: 11 additions & 41 deletions src/lib/log/logLevel.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
import { datadogLogs } from '@datadog/browser-logs';
import { RateLimit } from 'async-sema';
import type { Logger, LogLevelDesc } from 'loglevel';
import _log from 'loglevel';

import { isNodeEnv, isProdEnv, isStagingEnv } from '../../config/constants';

import { formatLog } from './logUtils';
import { sendToDatadog } from './sendToDatadog';

const ERRORS_WEBHOOK =
'https://discord.com/api/webhooks/898365255703470182/HqS3KqH_7-_dj0KYR6EzNqWhkH0yX6kvV_P32sZ3gnvB8M4AyMoy7W9bbjIul3Hmyu98';
const originalFactory = _log.methodFactory;
const enableDiscordAlerts = isProdEnv && isNodeEnv;
const enableDatadogLogs = isProdEnv && !isNodeEnv;

// requests per second = 35, timeUnit = 1sec
const discordRateLimiter = RateLimit(30);

/**
* Enable formatting special logs for Datadog in production
* Example:
Expand Down Expand Up @@ -45,17 +39,22 @@ export function apply(log: Logger, logPrefix: string = '') {
});
originalMethod.apply(null, args);

// post errors to Discord
if (isProdEnv && methodName === 'error' && enableDiscordAlerts) {
sendErrorToDiscord(ERRORS_WEBHOOK, message, opt).catch((error) => {
logErrorPlain('Error posting to discord', { originalMessage: message, error });
if (isProdEnv) {
const args2 = formatLog(message, opt, {
formatLogsForDocker: false,
isNodeEnv: false,
logPrefix,
methodName
});
sendToDatadog(methodName, args2[0], args2[1]);
}
};
};

log.setLevel(log.getLevel()); // Be sure to call setLevel method in order to apply plugin
} else if (enableDatadogLogs) {
}
// send logs to Datadog in production from browser clients
else if (enableDatadogLogs) {
log.methodFactory = (methodName, logLevel, loggerName) => {
const originalMethod = originalFactory(methodName, logLevel, loggerName);
return (message, ...args) => {
Expand All @@ -82,32 +81,3 @@ function logErrorPlain(message: string, opts: any) {
})
);
}

async function sendErrorToDiscord(webhook: string, message: any, opt: any) {
const fields: { name: string; value?: string }[] = [];
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
// const http = require('../http');
// if (opt instanceof Error) {
// fields = [
// { name: 'Error', value: opt.message },
// { name: 'Stacktrace', value: opt.stack?.slice(0, 500) }
// ];
// } else if (opt) {
// fields = Object.entries<any>(opt)
// .map(([name, _value]) => {
// const value = typeof _value === 'string' ? _value.slice(0, 500) : JSON.stringify(_value || {});
// return { name, value };
// })
// .slice(0, 5); // add a sane max # of fields just in case
// }
// await discordRateLimiter();
// return http.POST(webhook, {
// embeds: [
// {
// color: 14362664, // #db2828
// description: message,
// fields
// }
// ]
// });
}
4 changes: 2 additions & 2 deletions src/lib/log/logUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DateTime } from 'luxon';

export const TIMESTAMP_FORMAT = 'yyyy-LL-dd HH:mm:ss';

type LogMeta = {
export type LogMeta = {
data?: any;
error?: { message: string; code?: number; stack?: string };
};
Expand Down Expand Up @@ -66,7 +66,7 @@ export function formatTime(date: DateTime) {
}

// Check if value is primitive value
function _isPrimitiveValue(value: unknown): boolean {
export function _isPrimitiveValue(value: unknown): boolean {
return (
typeof value === 'symbol' ||
typeof value === 'string' ||
Expand Down
57 changes: 57 additions & 0 deletions src/lib/log/sendToDatadog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable no-console */
import { _isPrimitiveValue, type LogMeta } from './logUtils';

type DatadogLogPayload = {
message: string;
level: string;
timestamp: number;
context?: string;
data?: any;
service: string;
ddsource: string;
ddtags: string;
};

const env = process.env.REACT_APP_APP_ENV || process.env.NODE_ENV || 'unknown';
const service = process.env.DD_SERVICE || 'unknown';

const ddtags = `env:${env}`;

export async function sendToDatadog(level: string, log: string, context?: any) {
if (!process.env.DD_API_KEY) {
return;
}

let error: LogMeta['error'] = (context as LogMeta | undefined)?.error;
const maybeError = (context as { error?: Error })?.error || context;
if (maybeError instanceof Error) {
error = { ...maybeError, message: maybeError.message, stack: maybeError.stack };
}
if (_isPrimitiveValue(context) || context instanceof Array) {
context = { data: context };
}

const logItem: DatadogLogPayload = {
ddsource: 'nodejs',
...context,
message: log,
error,
hostName: process.env.HOSTNAME, // defined in beanstalk
service,
ddtags,
level,
timestamp: Date.now()
};

const response = await fetch('https://http-intake.logs.datadoghq.com/api/v2/logs', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'DD-API-KEY': process.env.DD_API_KEY
},
body: JSON.stringify(logItem)
});
if (!response.ok) {
console.error(`Error sending log to Datadog: ${response.status} - ${response.statusText}`);
}
}
14 changes: 14 additions & 0 deletions src/logTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { log } from './lib/log/log';

function main() {
log.info('test');
log.error('test', { error: new Error('test error') });
log.warn('test', new Error('test error inline'));
log.debug('test', ['test', 'test2']);
log.trace('test', 'another string');
setTimeout(() => {
process.exit(0);
}, 1000);
}

main();