-
Notifications
You must be signed in to change notification settings - Fork 203
fix(logger): add Pino PagerDuty V2 transport support #4914
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
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e7106a3
fix(logger): pino pagerduty transport
Reinis-FRP 4a52056
wip
Reinis-FRP 6acb1b1
wip(logger): add pino pagerduty v2 transport with shared config
Reinis-FRP a496d25
fix(logger): use import type syntax for linter compatibility
Reinis-FRP 928064e
refactor(logger): remove unnecessary PagerDuty re-exports
Reinis-FRP 86e36e7
refactor(logger): remove unused environment param from Pino Transport…
Reinis-FRP 349e9f4
fix(logger): properly mutate obj.mrkdwn in Pino PagerDuty transport
Reinis-FRP 12ca0c6
refactor(logger): simplify PagerDuty event sending with shared helper
Reinis-FRP e7f3b8e
refactor(logger): move markdown cleaning into sendPagerDutyEvent
Reinis-FRP ff09b1a
refactor(logger): move PagerDuty shared code to shared/PagerDutyV2Tra…
Reinis-FRP 1cdbd38
refactor(logger): simplify PagerDuty payload construction
Reinis-FRP a292bff
fix(logger): add missing semicolon
Reinis-FRP 01edd52
fix(logger): fix Pino PagerDuty transport to correctly parse logs
Reinis-FRP 10c160b
feat(logger): add error serializers to Pino logger
Reinis-FRP 7a82824
refactor(logger): use Pino levels.labels in convertLevelToSeverity
Reinis-FRP 4f2f9fb
fix(logger): correct severity mapping for unmapped log levels
Reinis-FRP ef55b58
test(logger): add comprehensive tests for PagerDuty V2 transports
Reinis-FRP 9f838c1
revert(discord-ticket-api): revert server.ts to master version
Reinis-FRP c98e474
fix(logger): address PR review feedback for PagerDuty transport
Reinis-FRP 8b919c5
test(logger): add tests for PAGER_DUTY_V2_CONFIG error handling
Reinis-FRP File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // This transport enables pino logging to send messages to PagerDuty v2 API. | ||
| // Pino transports run in worker threads for performance, so they can import dependencies. | ||
| import build from "pino-abstract-transport"; | ||
| import type { Transform } from "stream"; | ||
| import type { Config } from "../shared/PagerDutyV2Transport"; | ||
| import { createConfig, sendPagerDutyEvent } from "../shared/PagerDutyV2Transport"; | ||
|
|
||
| export default async function (opts: Config): Promise<Transform & build.OnUnknown> { | ||
| const config = createConfig(opts); | ||
|
|
||
| return build(async function (source) { | ||
| for await (const obj of source) { | ||
| try { | ||
| // Get routing key from custom services or use default integration key | ||
| const routing_key = config.customServices?.[obj.notificationPath] ?? config.integrationKey; | ||
| await sendPagerDutyEvent(routing_key, obj); | ||
| } catch (error) { | ||
| // Always log transport errors in Pino since there's no callback mechanism like Winston | ||
| console.error("PagerDuty v2 transport error:", error, { logObj: obj }); | ||
| } | ||
| } | ||
| }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { transport, TransportTargetOptions } from "pino"; | ||
| import type { Config as PagerDutyV2Config } from "../shared/PagerDutyV2Transport"; | ||
| import { createConfig as pagerDutyV2CreateConfig } from "../shared/PagerDutyV2Transport"; | ||
| import { isRecordStringUnknown } from "@uma/common"; | ||
| import dotenv from "dotenv"; | ||
| import minimist from "minimist"; | ||
| import path from "path"; | ||
|
|
||
| dotenv.config(); | ||
| const argv = minimist(process.argv.slice(), {}); | ||
|
|
||
| interface TransportsConfig { | ||
| level?: string; | ||
| pagerDutyV2Config?: PagerDutyV2Config & { disabled?: boolean }; | ||
| } | ||
|
|
||
| export function createPinoTransports(transportsConfig: TransportsConfig = {}): ReturnType<typeof transport> { | ||
| const targets: TransportTargetOptions[] = []; | ||
| const level = transportsConfig.level || process.env.LOG_LEVEL || "info"; | ||
|
|
||
| // stdout transport (for GCP Logging and local dev) | ||
| targets.push({ | ||
| target: "pino/file", | ||
| level, | ||
| options: { destination: 1 }, | ||
| }); | ||
|
|
||
| // Skip additional transports in test environment | ||
| if (argv._.indexOf("test") === -1) { | ||
| // Add PagerDuty V2 transport if configured | ||
| if (transportsConfig.pagerDutyV2Config || process.env.PAGER_DUTY_V2_CONFIG) { | ||
| // to disable pdv2, pass in a "disabled=true" in configs or env. | ||
| let pagerDutyV2Config; | ||
| try { | ||
| pagerDutyV2Config = | ||
| transportsConfig.pagerDutyV2Config ?? JSON.parse(process.env.PAGER_DUTY_V2_CONFIG || "null"); | ||
| if (!isRecordStringUnknown(pagerDutyV2Config)) { | ||
| throw new Error("Invalid PAGER_DUTY_V2_CONFIG object"); | ||
| } | ||
| } catch (error) { | ||
| if (error instanceof SyntaxError) { | ||
| throw new Error(`Failed to parse PAGER_DUTY_V2_CONFIG environment variable as JSON: ${error.message}`); | ||
| } | ||
| throw error; | ||
| } | ||
| const { disabled, ...config } = pagerDutyV2Config; | ||
| // this will throw an error if an invalid configuration is present | ||
| if (!disabled) { | ||
| targets.push({ | ||
| target: path.join(__dirname, "PagerDutyV2Transport.js"), | ||
| level: "error", | ||
| options: pagerDutyV2CreateConfig(config), | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return transport({ targets }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Shared PagerDuty V2 configuration and utilities | ||
| // Used by both Winston and Pino PagerDuty transports | ||
| import * as ss from "superstruct"; | ||
| import { event } from "@pagerduty/pdjs"; | ||
| import { levels } from "pino"; | ||
| import { removeAnchorTextFromLinks } from "../logger/Formatters"; | ||
|
|
||
| export type Severity = "critical" | "error" | "warning" | "info"; | ||
| export type Action = "trigger" | "acknowledge" | "resolve"; | ||
|
|
||
| const Config = ss.object({ | ||
| integrationKey: ss.string(), | ||
| customServices: ss.optional(ss.record(ss.string(), ss.string())), | ||
| logTransportErrors: ss.optional(ss.boolean()), | ||
| }); | ||
|
|
||
| export type Config = ss.Infer<typeof Config>; | ||
|
|
||
| // This turns an unknown (like json parsed data) into a config, or throws an error | ||
| export function createConfig(config: unknown): Config { | ||
| return ss.create(config, Config); | ||
| } | ||
|
|
||
| // PD v2 severity only supports critical, error, warning or info. | ||
| // Handles both Winston string levels and Pino numeric levels. | ||
| export function convertLevelToSeverity(level?: string | number): Severity { | ||
| if (!level) return "info"; | ||
|
|
||
| // Convert numeric Pino levels to string names using Pino's built-in mapping | ||
| const levelStr = typeof level === "number" ? levels.labels[level] : String(level).toLowerCase(); | ||
|
|
||
| // Map level names to PagerDuty severity values | ||
| if (levelStr === "fatal") return "critical"; | ||
| if (levelStr === "error") return "error"; | ||
| if (levelStr === "warn") return "warning"; | ||
| if (levelStr === "info") return "info"; | ||
| if (levelStr === "critical") return "critical"; | ||
|
|
||
| // Unknown/unmapped levels (debug, trace, etc.) default to lowest severity | ||
| return "info"; | ||
| } | ||
|
|
||
| // Send event to PagerDuty V2 API | ||
| // Accepts the whole log object and routing key, extracts necessary fields | ||
| export async function sendPagerDutyEvent(routing_key: string, logObj: any): Promise<void> { | ||
| // PagerDuty does not support anchor text in links, so we remove it from markdown if it exists. | ||
| if (typeof logObj.mrkdwn === "string") { | ||
| logObj.mrkdwn = removeAnchorTextFromLinks(logObj.mrkdwn); | ||
| } | ||
|
|
||
| // Convert numeric Pino levels to strings for summary (Winston already uses strings) | ||
| const levelStr = typeof logObj.level === "number" ? levels.labels[logObj.level] : logObj.level; | ||
|
|
||
| const payload: any = { | ||
| summary: `${levelStr}: ${logObj.at} ⭢ ${logObj.message}`, | ||
| severity: convertLevelToSeverity(logObj.level), | ||
| source: logObj["bot-identifier"] ? logObj["bot-identifier"] : undefined, | ||
| custom_details: logObj, | ||
| }; | ||
|
|
||
| await event({ | ||
| data: { | ||
| routing_key, | ||
| event_action: "trigger" as Action, | ||
| payload, | ||
| }, | ||
| }); | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice Winston was returning an error if if (!level), but agree it makes more sense to return info here