Skip to content

Commit

Permalink
Add tags in HB appender (#383)
Browse files Browse the repository at this point in the history
* Refactor HB appended

* Add tags in HB appender

* Append docs
  • Loading branch information
nikostoulas authored Jun 21, 2024
1 parent 7afb9e4 commit 92313b7
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 102 deletions.
7 changes: 6 additions & 1 deletion docs/integrations/honeybadger.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ Just setting the env var in config will suffice.
You can use orka's logger see [logs](https://workable.github.io/orka/logs) to log errors to HB

```js
logger.error(error, 'augment error message', { customKey: {}, action: 'action', component: 'logger-category' });
logger.error(error, 'augment error message', { customKey: {}, action: 'action', component: 'logger-category', tags: ['tag'] });
```

any custom keys given will be added to context sent to HB.

You can add tags in 3 ways:
- The error has a tags property
- You pass tags like above
- You add honeybadgerTags key with array of tags in (requestContext)[https://github.com/Workable/orka/blob/b76ca8da9fbc87aa2368f8fe3338d7cfdccac64d/docs/request-context.md?plain=1#L135]
85 changes: 50 additions & 35 deletions src/initializers/log4js/honeybadger-appender.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,77 @@
import * as Honeybadger from '@honeybadger-io/js';
import { getRequestContext } from '../../builder';

const Levels = require('log4js/lib/levels');
const log4jsErrorLevel = Levels.ERROR.level;

function notifyHoneybadger(categoryName, error, ...rest) {
if (!error) return;
error = buildError(error, rest);

let context = buildContext(rest);
let payload = buildPayload(categoryName, error, context);

Honeybadger.notify(error, payload);
}

function buildError(error: Error | string, rest: any[]) {
if (typeof error === 'string') {
error = new Error(error);
}

if (!error) {
return;
}
const message = rest.filter(r => typeof r === 'string').join('. ');
if (message) error.message = `${error.message}. ${message}`;

let context = {} as any;
let message = error.message;
rest.forEach(r => {
if (typeof r === 'string') {
message += `. ${r}`;
} else {
Object.assign(context, r);
}
});
return error;
}

function buildContext(rest: any[]) {
return rest.filter(r => typeof r !== 'string').reduce((acc, r) => Object.assign(acc, r), {});
}

function buildPayload(categoryName, error, context) {
let actionFallback = context.action;
let componentFallback = context.component;
let componentFallback = context.component || categoryName;
let tags: string[] = context.tags || [];

delete context.action;
delete context.component;
delete context.tags;

let { headers = {}, action = actionFallback, component = componentFallback, params = {}, name } = error;
let { headers = {}, params = {} } = error;
error.action ||= actionFallback;
error.component ||= componentFallback;

Object.assign(context, error.context);
Object.assign(headers, context.headers);
Object.assign(params, context.params);
tags = tags.concat(error.tags || []);
tags = tags.concat(getRequestContext()?.get('honeybadgerTags') || []);

const computedComponent = component || categoryName;

const fingerprint = context.fingerprint || generateFingerprint(name, computedComponent, action) || categoryName;
const fingerprint = generateFingerprint(categoryName, error, context);
delete context.fingerprint;

Honeybadger.notify(
{ stack: error.stack, message, name },
{
context,
headers,
cgiData: {
'server-software': `Node ${process.version}`
},
action,
component: computedComponent,
params,
fingerprint
}
);
return {
context,
headers,
cgiData: {
'server-software': `Node ${process.version}`
},
action: error.action,
component: error.component,
params,
tags,
fingerprint
};
}

const generateFingerprint = (name, component, action) => {
if (!action || !component) {
return;
}
const generateFingerprint = (categoryName, error, context) => {
if (context.fingerprint) return context.fingerprint;
const action = error.action;
const component = error.component;
const name = error.name;

if (!action || !component) return categoryName;

if (name && name !== 'Error') {
return `${name}_${component}_${action}`;
Expand Down
58 changes: 29 additions & 29 deletions test/examples/json-appender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('json-appender', function () {
text.should.eql('default body');
const cleanStack = msg => {
const stack = JSON.parse(msg);
stack.stack_trace = stack.stack_trace.substring(0, 31);
stack.stack_trace = stack.stack_trace?.substring(0, 31);
return stack;
};
logSpy.args
Expand All @@ -134,35 +134,35 @@ describe('json-appender', function () {
message: 'test - this was a test warning',
stack_trace: 'Error: test\n at /logWarning ',
context: pickBy({
propagatedHeaders: { 'x-orka-request-id': 'test-id' },
requestId: 'test-id',
context: 'foo'
propagatedHeaders: { 'x-orka-request-id': 'test-id' },
requestId: 'test-id',
context: 'foo'
})
}
],
[
{
timestamp: '2019-01-01T00:00:00.000Z',
severity: 'ERROR',
categoryName: 'orka.errorHandler',
message: 'test',
stack_trace: 'Error: test\n at /logWarning ',
context: pickBy(
}
],
[
{
expose: false,
statusCode: 505,
status: 505,
component: 'koa',
action: '/logWarning',
params: { path: {}, query: {}, body: {}, requestId: 'test-id' },
propagatedHeaders: { 'x-orka-request-id': 'test-id' },
state: { riviereStartedAt: 1546300800000, requestId: 'test-id' },
requestId: 'test-id'
},
_ => _ !== undefined
)
}
]
]);
timestamp: '2019-01-01T00:00:00.000Z',
severity: 'ERROR',
categoryName: 'orka.errorHandler',
message: 'test',
stack_trace: 'Error: test\n at /logWarning ',
context: pickBy(
{
expose: false,
statusCode: 505,
status: 505,
component: 'koa',
action: '/logWarning',
params: { path: {}, query: {}, body: {}, requestId: 'test-id' },
propagatedHeaders: { 'x-orka-request-id': 'test-id' },
state: { riviereStartedAt: 1546300800000, requestId: 'test-id' },
requestId: 'test-id'
},
_ => _ !== undefined
)
}
]
]);
});
});
82 changes: 45 additions & 37 deletions test/initializers/log4js/honeybadger-appender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as appender from '../../../src/initializers/log4js/honeybadger-appender
import 'should';
import * as Honeybadger from '@honeybadger-io/js';
import * as sinon from 'sinon';
import { runWithContext } from '../../../src/builder';

const sandbox = sinon.createSandbox();

Expand Down Expand Up @@ -46,6 +47,7 @@ describe('log4js_honeybadger_appender', () => {
action: undefined,
component: 'testCategoryName',
params: {},
tags: [],
fingerprint: 'testCategoryName'
});
});
Expand Down Expand Up @@ -82,6 +84,7 @@ describe('log4js_honeybadger_appender', () => {
action: '/test/endpoint',
component: 'testCategoryName',
params: {},
tags: [],
fingerprint: 'testCategoryName_/test/endpoint'
});
});
Expand Down Expand Up @@ -135,7 +138,7 @@ describe('log4js_honeybadger_appender', () => {
level: 40000
},
categoryName: 'testCategoryName',
data: [err, {fingerprint: 'CustomError'}]
data: [err, { fingerprint: 'CustomError' }]
});
notifySpy.callCount.should.equal(1);
notifySpy.args[0][1].fingerprint.should.equal('CustomError');
Expand All @@ -161,44 +164,49 @@ describe('log4js_honeybadger_appender', () => {

it('should assign any additional json values to the context', () => {
const err = new Error('omg') as any;
err.status = 200;
err.component = 'testController';
err.action = '/test/endpoint';
err.context = {
something: 'ok'
};
appender.configure()({
level: {
level: 40000
},
categoryName: 'testCategoryName',
data: [
err,
{
runWithContext(new Map([['honeybadgerTags', ['context-tag']]]), () => {
err.status = 200;
err.tags = ['error-tag'];
err.component = 'testController';
err.action = '/test/endpoint';
err.context = {
something: 'ok'
};
appender.configure()({
level: {
level: 40000
},
categoryName: 'testCategoryName',
data: [
err,
{
a: 'aOK',
b: 'bOK'
},
{
c: 'cOK'
},
{ tags: ['tag3'] }
]
});
notifySpy.callCount.should.equal(1);
notifySpy.args[0][1].should.eql({
context: {
a: 'aOK',
b: 'bOK'
b: 'bOK',
c: 'cOK',
something: 'ok'
},
{
c: 'cOK'
}
]
});
notifySpy.callCount.should.equal(1);
notifySpy.args[0][1].should.eql({
context: {
a: 'aOK',
b: 'bOK',
c: 'cOK',
something: 'ok'
},
headers: {},
cgiData: {
'server-software': `Node ${process.version}`
},
action: '/test/endpoint',
component: 'testController',
params: {},
fingerprint: 'testController_/test/endpoint'
headers: {},
cgiData: {
'server-software': `Node ${process.version}`
},
action: '/test/endpoint',
component: 'testController',
params: {},
tags: ['tag3', 'error-tag', 'context-tag'],
fingerprint: 'testController_/test/endpoint'
});
});
});
});

0 comments on commit 92313b7

Please sign in to comment.