Skip to content

Commit

Permalink
feat: tolerate dirty json (#431)
Browse files Browse the repository at this point in the history
When you use the `aws logs tail` command, it outputs non-JSON along with
the JSON log entries. For example:

```bash
2024-01-01T19:52:19 {"level":20,"time":1704138739937,"pid":8,"hostname":"169.254.43.189","source":"search/src/lib/typesense.ts","msg":"found 20 records not in index"}
```

This change allows piping this type of output and having pjl still be
able to parse the log entry. In theory, we could output the extraneous
non-JSON data to stdout, but I don't personally need that, so I didn't
implement it.

FYI, I use pino to output JSON log entries from an AWS Lambda function.

---------

Co-authored-by: Blayne Chard <[email protected]>
  • Loading branch information
dprothero and blacha authored Jan 2, 2024
1 parent 3fbba16 commit 07db8f7
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"scripts": {
"build": "tsc --pretty",
"lint": "eslint . --fix --ignore-path .gitignore",
"test": "node --test"
"test": "npm run build && node --test"
},
"repository": {
"url": "https://github.com/blacha/pretty-json-log"
Expand Down
8 changes: 5 additions & 3 deletions src/pretty/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ function getLogStatus(level: number): string {
return c.bgRed('FATAL');
}

type LogObject = Record<string, unknown>;

export const PrettySimplePino = {
Ignore: new Set<string>([
// pino formats
Expand All @@ -22,7 +24,7 @@ export const PrettySimplePino = {
'name',
'msg',
]),
formatObject(obj: Record<string, any>, prefix = ''): string[] {
formatObject(obj: LogObject, prefix = ''): string[] {
const kvs = [];
for (const key of Object.keys(obj)) {
if (PrettySimplePino.Ignore.has(key)) continue;
Expand All @@ -35,9 +37,9 @@ export const PrettySimplePino = {
if (typeofValue === 'number') {
output = c.yellow(String(value));
} else if (typeofValue === 'string') {
output = c.green(value);
output = c.green(value as string);
} else if (typeofValue === 'object') {
const subOutput = this.formatObject(value, prefix);
const subOutput = this.formatObject(value as LogObject, prefix);
if (subOutput.length > 0) {
output = `{ ${subOutput.join(' ')} }`;
}
Expand Down
23 changes: 23 additions & 0 deletions src/transform.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import assert from 'node:assert';
import { describe, it } from 'node:test';

import { tryGetJson } from './transform.js';

describe('transform', () => {
describe('tryGetJson', () => {
it('returns null for invalid JSON', () => {
assert.strictEqual(tryGetJson(''), null);
assert.strictEqual(tryGetJson('foo'), null);
assert.strictEqual(tryGetJson('{"foo":'), null);
assert.strictEqual(tryGetJson('2024-01-01T19:53:58 {"foo":'), null);
assert.strictEqual(tryGetJson('2024-01-01T19:53:58 {"foo": "bar"}}'), null);
});

it('returns the parsed JSON', () => {
assert.deepStrictEqual(tryGetJson('{}'), {});
assert.deepStrictEqual(tryGetJson('{"foo": "bar"}'), { foo: 'bar' });
assert.deepStrictEqual(tryGetJson('2024-01-01T19:53:58 {"foo": "bar"}'), { foo: 'bar' });
assert.deepStrictEqual(tryGetJson('2024-01-01T19:53:58 {"foo": "bar"} garbage'), { foo: 'bar' });
});
});
});
10 changes: 8 additions & 2 deletions src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import { StringDecoder } from 'string_decoder';
import { LogMessage, LogSkipLine } from './msg.js';
import { PrettySimple } from './pretty/simple.js';

function tryGetJson(data: string): null | Record<string, unknown> {
export function tryGetJson(data: string): null | Record<string, unknown> {
const jsonStart = data.indexOf('{');
if (jsonStart === -1) return null;

const jsonEnd = data.lastIndexOf('}');
if (jsonEnd === -1) return null;

try {
return JSON.parse(data);
return JSON.parse(data.slice(jsonStart, jsonEnd + 1));
} catch {
return null;
}
Expand Down

0 comments on commit 07db8f7

Please sign in to comment.