feat!: API options object support to control sentry handling; removal of logger.sentry.
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/push/publish unknown status

This commit is contained in:
2026-08-07 12:07:29 -03:00
parent 9f18e0855e
commit 6bee3a801d
11 changed files with 1170 additions and 161 deletions
+98 -12
View File
@@ -1,28 +1,114 @@
import { describe, expect, it, vi } from "vitest";
import { createLogger } from "../src/index.js";
import { createSentrySink } from "../src/sentry.js";
import { createLogger } from "../src/index";
import { createSentrySink, toSentryLogPayload } from "../src/sentry";
describe("createSentrySink", () => {
it("captures errors and explicitly selected events with namespace and structured context", () => {
const scope = { setLevel: vi.fn(), setTag: vi.fn(), setExtras: vi.fn() };
const sentry = {
function createFakeSentry() {
const scope = { setLevel: vi.fn(), setTag: vi.fn(), setExtras: vi.fn() };
return {
scope,
sentry: {
withScope: (callback: (value: typeof scope) => void) => callback(scope),
captureException: vi.fn(),
captureMessage: vi.fn(),
};
logger: {
trace: vi.fn(),
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
},
};
}
describe("toSentryLogPayload", () => {
it("joins string messages and flattens primitive object attributes", () => {
expect(
toSentryLogPayload({
level: "info",
namespace: "API",
arguments: ["Cache warmed", { entries: 42, ok: true, nested: { a: 1 } }],
timestamp: new Date(),
environment: "production",
sendToSentryLogs: true,
sendToSentryIssue: false,
sendToConsole: false,
}),
).toEqual({
message: "Cache warmed",
attributes: {
"logger.namespace": "API",
entries: 42,
ok: true,
},
});
});
});
describe("createSentrySink", () => {
it("captures errors as Issues with namespace and structured attributes", () => {
const { scope, sentry } = createFakeSentry();
const logger = createLogger({
environment: "production",
namespace: "API",
sinks: [createSentrySink(sentry)],
});
const error = new Error("offline");
logger.warn("ignored");
logger.sentry.info("Cache warmed");
logger.warn("ignored without logs");
logger.error("Request failed", error, { requestId: "req_1" });
expect(sentry.captureMessage).toHaveBeenCalledWith("Cache warmed", "info");
expect(sentry.captureException).toHaveBeenCalledWith(error);
expect(scope.setLevel).toHaveBeenCalledWith("info");
expect(sentry.captureMessage).not.toHaveBeenCalled();
expect(sentry.logger.info).not.toHaveBeenCalled();
expect(scope.setLevel).toHaveBeenCalledWith("error");
expect(scope.setTag).toHaveBeenCalledWith("logger.namespace", "API");
expect(scope.setExtras).toHaveBeenCalledWith({ context_0: { requestId: "req_1" } });
expect(scope.setExtras).toHaveBeenCalledWith({
"logger.namespace": "API",
requestId: "req_1",
});
});
it("writes non-error events to Sentry Logs when enabled, not as Issues", () => {
const { sentry } = createFakeSentry();
const logger = createLogger({
environment: "production",
namespace: "API",
sinks: [createSentrySink(sentry, { logs: true })],
});
logger.warn("slow", { ms: 1200 });
logger.info("forced", { id: 1 }, { sentry: true });
expect(sentry.captureMessage).not.toHaveBeenCalled();
expect(sentry.captureException).not.toHaveBeenCalled();
expect(sentry.logger.warn).toHaveBeenCalledWith("slow", {
"logger.namespace": "API",
ms: 1200,
});
expect(sentry.logger.info).toHaveBeenCalledWith("forced", {
"logger.namespace": "API",
id: 1,
});
});
it("does not create an Issue when suppressSentry is set", () => {
const { sentry } = createFakeSentry();
const logger = createLogger({
environment: "production",
runtime: "node",
sinks: [createSentrySink(sentry, { logs: true })],
});
logger.error("expected", new Error("nope"), { suppressSentry: true });
expect(sentry.captureException).not.toHaveBeenCalled();
expect(sentry.logger.error).not.toHaveBeenCalled();
});
it("never sends in development", () => {
const { sentry } = createFakeSentry();
const logger = createLogger({
environment: "development",
sinks: [createSentrySink(sentry, { logs: true })],
});
logger.error("local", new Error("x"));
logger.warn("local warn", { sentry: true });
expect(sentry.captureException).not.toHaveBeenCalled();
expect(sentry.logger.warn).not.toHaveBeenCalled();
});
});