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

feat(pino): bump pino dependency #188

Open
wants to merge 4 commits into
base: main
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
6 changes: 3 additions & 3 deletions cli.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const pump = require("pump");
const split = require("split2");

const { getTransformStream } = require("./");
const getTransformStream = require("./");

const options = {
logFormat: process.env.LOG_FORMAT,
logLevelInString: process.env.LOG_LEVEL_IN_STRING,
sentryDsn: process.env.SENTRY_DSN,
};

pump(process.stdin, split(), getTransformStream(options), process.stdout);
const res = getTransformStream(opts)
pump(process.stdin, res)
6 changes: 4 additions & 2 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"use strict";

var pino = require("pino")({
name: "probot",
const pino = require("pino")({
transport: {
target: './index.js'
},
});

// simulate probot.log.info()
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export type Options = {
};

export function getTransformStream(options?: Options): Transform;
export default getTransformStream;
231 changes: 161 additions & 70 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module.exports = { getTransformStream };
module.exports = getTransformStream;

const { Transform } = require("readable-stream");

const prettyFactory = require("pino-pretty");
const { isMainThread } = require('worker_threads');
const { prettyFactory } = require("pino-pretty");
const abstractTransport = require('pino-abstract-transport');
const pump = require('pump');
const SonicBoom = require('sonic-boom');
const Sentry = require("@sentry/node");

const LEVEL_MAP = {
Expand All @@ -14,6 +17,70 @@ const LEVEL_MAP = {
60: "fatal",
};

function noop() { }

/**
* Creates a safe SonicBoom instance
*
* @param {object} opts Options for SonicBoom
*
* @returns {object} A new SonicBoom stream
*/
function buildSafeSonicBoom(opts) {
const stream = new SonicBoom(opts)
stream.on('error', filterBrokenPipe)
// if we are sync: false, we must flush on exit
if (!opts.sync && isMainThread) {
setupOnExit(stream)
}
return stream

function filterBrokenPipe(err) {
if (err.code === 'EPIPE') {
stream.write = noop
stream.end = noop
stream.flushSync = noop
stream.destroy = noop
return
}
stream.removeListener('error', filterBrokenPipe)
}
}

function setupOnExit(stream) {
/* istanbul ignore next */
if (global.WeakRef && global.WeakMap && global.FinalizationRegistry) {
// This is leak free, it does not leave event handlers
const onExit = require('on-exit-leak-free')

onExit.register(stream, autoEnd)

stream.on('close', function () {
onExit.unregister(stream)
})
}
}

/* istanbul ignore next */
function autoEnd(stream, eventName) {
// This check is needed only on some platforms

if (stream.destroyed) {
return
}

if (eventName === 'beforeExit') {
// We still have an event loop, let's use it
stream.flush()
stream.on('drain', function () {
stream.end()
})
} else {
// We do not have an event loop, so flush synchronously
stream.flushSync()
}
}

/**
* Implements Probot's default logging formatting and error captionaing using Sentry.
*
Expand Down Expand Up @@ -52,85 +119,109 @@ function getTransformStream(options = {}) {
),
});

return new Transform({
objectMode: true,
transform(chunk, enc, cb) {
const line = chunk.toString().trim();

/* istanbul ignore if */
if (line === undefined) return cb();

const data = sentryEnabled ? JSON.parse(line) : null;

if (!sentryEnabled || data.level < 50) {
if (formattingEnabled) {
return cb(null, pretty(line));
}
return abstractTransport(function (source) {
const stream = new Transform({
objectMode: true,
autoDestroy: true,
transform(chunk, enc, cb) {
const line = chunk.toString().trim();

if (levelAsString) {
return cb(null, stringifyLogLevel(JSON.parse(line)));
}
/* istanbul ignore if */
if (line === undefined) return cb();

cb(null, line + "\n");
return;
}
const data = sentryEnabled ? JSON.parse(line) : null;

Sentry.withScope(function (scope) {
const sentryLevelName =
data.level === 50 ? Sentry.Severity.Error : Sentry.Severity.Fatal;
scope.setLevel(sentryLevelName);
if (!sentryEnabled || data.level < 50) {
if (formattingEnabled) {
return cb(null, pretty(line));
}

for (const extra of ["event", "headers", "request", "status"]) {
if (!data[extra]) continue;
if (levelAsString) {
return cb(null, stringifyLogLevel(JSON.parse(line)));
}

scope.setExtra(extra, data[extra]);
cb(null, line + "\n");
return;
}

// set user id and username to installation ID and account login
if (data.event && data.event.payload) {
const {
// When GitHub App is installed organization wide
installation: { id, account: { login: account } = {} } = {},

// When the repository belongs to an organization
organization: { login: organization } = {},
// When the repository belongs to a user
repository: { owner: { login: owner } = {} } = {},
} = data.event.payload;

scope.setUser({
id: id,
username: account || organization || owner,
});
}
Sentry.withScope(function (scope) {
const sentryLevelName =
data.level === 50 ? Sentry.Severity.Error : Sentry.Severity.Fatal;
scope.setLevel(sentryLevelName);

for (const extra of ["event", "headers", "request", "status"]) {
if (!data[extra]) continue;

scope.setExtra(extra, data[extra]);
}

// set user id and username to installation ID and account login
if (data.event && data.event.payload) {
const {
// When GitHub App is installed organization wide
installation: { id, account: { login: account } = {} } = {},

// When the repository belongs to an organization
organization: { login: organization } = {},
// When the repository belongs to a user
repository: { owner: { login: owner } = {} } = {},
} = data.event.payload;

scope.setUser({
id: id,
username: account || organization || owner,
});
}

const sentryEventId = Sentry.captureException(toSentryError(data));

// reduce logging data and add reference to sentry event instead
if (data.event) {
data.event = { id: data.event.id };
}
if (data.request) {
data.request = {
method: data.request.method,
url: data.request.url,
};
}
data.sentryEventId = sentryEventId;

if (formattingEnabled) {
return cb(null, pretty(data));
}

// istanbul ignore if
if (levelAsString) {
return cb(null, stringifyLogLevel(data));
}

cb(null, JSON.stringify(data) + "\n");
});
},
});

const sentryEventId = Sentry.captureException(toSentryError(data));
let destination

// reduce logging data and add reference to sentry event instead
if (data.event) {
data.event = { id: data.event.id };
}
if (data.request) {
data.request = {
method: data.request.method,
url: data.request.url,
};
}
data.sentryEventId = sentryEventId;
if (typeof options.destination === 'object' && typeof options.destination.write === 'function') {
destination = options.destination
} else {
destination = buildSafeSonicBoom({
dest: options.destination || 1,
append: options.append,
mkdir: options.mkdir,
sync: options.sync // by default sonic will be async
})
}

if (formattingEnabled) {
return cb(null, pretty(data));
}
source.on('unknown', function (line) {
destination.write(line + '\n')
})

// istanbul ignore if
if (levelAsString) {
return cb(null, stringifyLogLevel(data));
}
pump(source, stream, destination)

cb(null, JSON.stringify(data) + "\n");
});
},
});
return stream;
}, { parse: 'lines' });
}

function stringifyLogLevel(data) {
Expand Down
Loading