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

Allow Pino transport to be used as transport #1151

Merged
merged 1 commit into from
Nov 22, 2024
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
50 changes: 50 additions & 0 deletions .changesets/allow-pino-transport-to-be-used-as-a-transport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
bump: patch
type: fix
---

Allow Pino transport to be used as a transport. Before, our Pino transport could only be used as a destination:

```js
import pino from "pino";
import { Appsignal, AppsignalPinoTransport } from "@appsignal/nodejs";

pino(AppsignalPinoTransport({
client: Appsignal.client,
group: "pino"
}));
```

This meant it was not possible to log both to our transport and to another destination.

Now, it is also possible to use it as a Pino transport, with the `transport` Pino config option or the `pino.transport()` function:

```js
import pino from "pino";

pino({
transport: {
target: "@appsignal/nodejs/pino",
options: {
group: "pino"
tombruijn marked this conversation as resolved.
Show resolved Hide resolved
}
}
});
```

It is no longer necessary to pass the AppSignal client to the Pino transport. AppSignal must be active for the Pino transport to work.

By enabling its use as a transport, it is now possible to use it alongside other transports:

```js
pino({
transport: {
targets: [
// Send logs to AppSignal...
{ target: "@appsignal/nodejs/pino" },
// ... and to standard output!
{ target: "pino/file" }
]
}
});
```
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*
!/dist/**/*
!/pino/**/*
!/scripts/**/*
scripts/**/*.test.js
!/ext/appsignal_extension.cpp
Expand Down
2 changes: 2 additions & 0 deletions pino/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict"
module.exports = require("../dist/pino_transport")
55 changes: 29 additions & 26 deletions src/pino_transport.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
import { LOGGER_LEVEL_SEVERITY } from "./logger"
import { LOGGER_LEVEL_SEVERITY, LOGGER_FORMAT } from "./logger"
import build from "pino-abstract-transport"
import { Client } from "./client"
import { Extension } from "./extension"

type PinoTransportOptions = {
client: Client
group: string
group?: string
}

const appsignalPinoTransport = ({
client,
group = "app"
}: PinoTransportOptions) => {
type LogData = {
severity: number
message: string
attributes: Record<string, any>
}

const appsignalPinoTransport = ({ group = "app" }: PinoTransportOptions) => {
return build(async (source: any) => {
tombruijn marked this conversation as resolved.
Show resolved Hide resolved
// We expect this code to be running in a worker thread and for the
// extension to have already been loaded and started on the main thread.
// Since we expect the extension to already have been initialised, we
// pass `{ active: false }` to avoid initialising it again.
const extension = new Extension({ active: false })

for await (const obj of source) {
sendLogs(parseInfo(obj, group), client)
sendLogs(extension, group, parseInfo(obj))
}
})
}

async function sendLogs(data: Record<string, any>, client: Client) {
client.extension.log(
data.group || "app",
data.severity,
0,
data.msg,
data.attributes
)
}
async function sendLogs(extension: Extension, group: string, data: LogData) {
extension.log(
group,
data.severity,
LOGGER_FORMAT.plaintext,
data.message,
data.attributes
)
}

function parseInfo(
obj: Record<string, any>,
group: string
): Record<string, any> {
const { hostname, level, msg, ...attributes } = obj
function parseInfo(obj: Record<string, any>): LogData {
const { level, msg, ...attributes } = obj

return {
severity: getSeverity(level),
hostname,
group,
msg,
message: msg,
attributes: flattenAttributes(attributes)
}
}
Expand Down
Loading