Skip to content

Commit cff47df

Browse files
authored
feat(node): Add support for winston logger (#15983)
resolves https://linear.app/getsentry/issue/JS-4 resolves #15953 This PR adds support for winston, a popular logging library for Node.js. Specifically you can send logs via winston to Sentry. Usage: ```js const winston = require('winston'); const Transport = require('winston-transport'); const transport = Sentry.createSentryWinstonTransport(Transport); const logger = winston.createLogger({ transports: [transport], }); ``` `createSentryWinstonTransport` was used because we didn't want to add `winston` as a dependency to the nodejs sdk.
1 parent 458fa07 commit cff47df

File tree

16 files changed

+602
-33
lines changed

16 files changed

+602
-33
lines changed

Diff for: dev-packages/node-integration-tests/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"reflect-metadata": "0.2.1",
6868
"rxjs": "^7.8.1",
6969
"tedious": "^18.6.1",
70+
"winston": "^3.17.0",
7071
"yargs": "^16.2.0"
7172
},
7273
"devDependencies": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import * as Sentry from '@sentry/node';
2+
import winston from 'winston';
3+
import Transport from 'winston-transport';
4+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
release: '1.0.0',
9+
environment: 'test',
10+
_experiments: {
11+
enableLogs: true,
12+
},
13+
transport: loggingTransport,
14+
});
15+
16+
async function run(): Promise<void> {
17+
// Create a custom transport that extends winston-transport
18+
const SentryWinstonTransport = Sentry.createSentryWinstonTransport(Transport);
19+
20+
// Create logger with default levels
21+
const logger = winston.createLogger({
22+
transports: [new SentryWinstonTransport()],
23+
});
24+
25+
// Test basic logging
26+
logger.info('Test info message');
27+
logger.error('Test error message');
28+
29+
// If custom levels are requested
30+
if (process.env.CUSTOM_LEVELS === 'true') {
31+
const customLevels = {
32+
levels: {
33+
error: 0,
34+
warn: 1,
35+
info: 2,
36+
http: 3,
37+
verbose: 4,
38+
debug: 5,
39+
silly: 6,
40+
},
41+
colors: {
42+
error: 'red',
43+
warn: 'yellow',
44+
info: 'green',
45+
http: 'magenta',
46+
verbose: 'cyan',
47+
debug: 'blue',
48+
silly: 'grey',
49+
},
50+
};
51+
52+
const customLogger = winston.createLogger({
53+
levels: customLevels.levels,
54+
transports: [new SentryWinstonTransport()],
55+
});
56+
57+
customLogger.info('Test info message');
58+
customLogger.error('Test error message');
59+
}
60+
61+
// If metadata is requested
62+
if (process.env.WITH_METADATA === 'true') {
63+
logger.info('Test message with metadata', {
64+
foo: 'bar',
65+
number: 42,
66+
});
67+
}
68+
69+
await Sentry.flush();
70+
}
71+
72+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
73+
void run();
+307
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
import { afterAll, describe, test, expect } from 'vitest';
2+
import { cleanupChildProcesses, createRunner } from '../../utils/runner';
3+
4+
describe('winston integration', () => {
5+
afterAll(() => {
6+
cleanupChildProcesses();
7+
});
8+
9+
test('should capture winston logs with default levels', async () => {
10+
const runner = createRunner(__dirname, 'subject.ts')
11+
.expect({
12+
otel_log: {
13+
severityText: 'info',
14+
body: {
15+
stringValue: 'Test info message',
16+
},
17+
attributes: [
18+
{
19+
key: 'sentry.origin',
20+
value: {
21+
stringValue: 'auto.logging.winston',
22+
},
23+
},
24+
{
25+
key: 'sentry.release',
26+
value: {
27+
stringValue: '1.0.0',
28+
},
29+
},
30+
{
31+
key: 'sentry.environment',
32+
value: {
33+
stringValue: 'test',
34+
},
35+
},
36+
{
37+
key: 'sentry.sdk.name',
38+
value: {
39+
stringValue: 'sentry.javascript.node',
40+
},
41+
},
42+
{
43+
key: 'sentry.sdk.version',
44+
value: {
45+
stringValue: expect.any(String),
46+
},
47+
},
48+
{
49+
key: 'server.address',
50+
value: {
51+
stringValue: expect.any(String),
52+
},
53+
},
54+
],
55+
},
56+
})
57+
.expect({
58+
otel_log: {
59+
severityText: 'error',
60+
body: {
61+
stringValue: 'Test error message',
62+
},
63+
attributes: [
64+
{
65+
key: 'sentry.origin',
66+
value: {
67+
stringValue: 'auto.logging.winston',
68+
},
69+
},
70+
{
71+
key: 'sentry.release',
72+
value: {
73+
stringValue: '1.0.0',
74+
},
75+
},
76+
{
77+
key: 'sentry.environment',
78+
value: {
79+
stringValue: 'test',
80+
},
81+
},
82+
{
83+
key: 'sentry.sdk.name',
84+
value: {
85+
stringValue: 'sentry.javascript.node',
86+
},
87+
},
88+
{
89+
key: 'sentry.sdk.version',
90+
value: {
91+
stringValue: expect.any(String),
92+
},
93+
},
94+
{
95+
key: 'server.address',
96+
value: {
97+
stringValue: expect.any(String),
98+
},
99+
},
100+
],
101+
},
102+
})
103+
.start();
104+
105+
await runner.completed();
106+
});
107+
108+
test('should capture winston logs with custom levels', async () => {
109+
const runner = createRunner(__dirname, 'subject.ts')
110+
.withEnv({ CUSTOM_LEVELS: 'true' })
111+
.expect({
112+
otel_log: {
113+
severityText: 'info',
114+
body: {
115+
stringValue: 'Test info message',
116+
},
117+
attributes: [
118+
{
119+
key: 'sentry.origin',
120+
value: {
121+
stringValue: 'auto.logging.winston',
122+
},
123+
},
124+
{
125+
key: 'sentry.release',
126+
value: {
127+
stringValue: '1.0.0',
128+
},
129+
},
130+
{
131+
key: 'sentry.environment',
132+
value: {
133+
stringValue: 'test',
134+
},
135+
},
136+
{
137+
key: 'sentry.sdk.name',
138+
value: {
139+
stringValue: 'sentry.javascript.node',
140+
},
141+
},
142+
{
143+
key: 'sentry.sdk.version',
144+
value: {
145+
stringValue: expect.any(String),
146+
},
147+
},
148+
{
149+
key: 'server.address',
150+
value: {
151+
stringValue: expect.any(String),
152+
},
153+
},
154+
],
155+
},
156+
})
157+
.expect({
158+
otel_log: {
159+
severityText: 'error',
160+
body: {
161+
stringValue: 'Test error message',
162+
},
163+
attributes: [
164+
{
165+
key: 'sentry.origin',
166+
value: {
167+
stringValue: 'auto.logging.winston',
168+
},
169+
},
170+
{
171+
key: 'sentry.release',
172+
value: {
173+
stringValue: '1.0.0',
174+
},
175+
},
176+
{
177+
key: 'sentry.environment',
178+
value: {
179+
stringValue: 'test',
180+
},
181+
},
182+
{
183+
key: 'sentry.sdk.name',
184+
value: {
185+
stringValue: 'sentry.javascript.node',
186+
},
187+
},
188+
{
189+
key: 'sentry.sdk.version',
190+
value: {
191+
stringValue: expect.any(String),
192+
},
193+
},
194+
{
195+
key: 'server.address',
196+
value: {
197+
stringValue: expect.any(String),
198+
},
199+
},
200+
],
201+
},
202+
})
203+
.start();
204+
205+
await runner.completed();
206+
});
207+
208+
test('should capture winston logs with metadata', async () => {
209+
const runner = createRunner(__dirname, 'subject.ts')
210+
.withEnv({ WITH_METADATA: 'true' })
211+
.expect({
212+
otel_log: {
213+
severityText: 'info',
214+
body: {
215+
stringValue: 'Test info message',
216+
},
217+
attributes: [
218+
{
219+
key: 'sentry.origin',
220+
value: {
221+
stringValue: 'auto.logging.winston',
222+
},
223+
},
224+
{
225+
key: 'sentry.release',
226+
value: {
227+
stringValue: '1.0.0',
228+
},
229+
},
230+
{
231+
key: 'sentry.environment',
232+
value: {
233+
stringValue: 'test',
234+
},
235+
},
236+
{
237+
key: 'sentry.sdk.name',
238+
value: {
239+
stringValue: 'sentry.javascript.node',
240+
},
241+
},
242+
{
243+
key: 'sentry.sdk.version',
244+
value: {
245+
stringValue: expect.any(String),
246+
},
247+
},
248+
{
249+
key: 'server.address',
250+
value: {
251+
stringValue: expect.any(String),
252+
},
253+
},
254+
],
255+
},
256+
})
257+
.expect({
258+
otel_log: {
259+
severityText: 'error',
260+
body: {
261+
stringValue: 'Test error message',
262+
},
263+
attributes: [
264+
{
265+
key: 'sentry.origin',
266+
value: {
267+
stringValue: 'auto.logging.winston',
268+
},
269+
},
270+
{
271+
key: 'sentry.release',
272+
value: {
273+
stringValue: '1.0.0',
274+
},
275+
},
276+
{
277+
key: 'sentry.environment',
278+
value: {
279+
stringValue: 'test',
280+
},
281+
},
282+
{
283+
key: 'sentry.sdk.name',
284+
value: {
285+
stringValue: 'sentry.javascript.node',
286+
},
287+
},
288+
{
289+
key: 'sentry.sdk.version',
290+
value: {
291+
stringValue: expect.any(String),
292+
},
293+
},
294+
{
295+
key: 'server.address',
296+
value: {
297+
stringValue: expect.any(String),
298+
},
299+
},
300+
],
301+
},
302+
})
303+
.start();
304+
305+
await runner.completed();
306+
});
307+
});

Diff for: packages/astro/src/index.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export {
8585
postgresIntegration,
8686
prismaIntegration,
8787
childProcessIntegration,
88+
createSentryWinstonTransport,
8889
redisIntegration,
8990
requestDataIntegration,
9091
rewriteFramesIntegration,

0 commit comments

Comments
 (0)