sourcebot/packages/logger/src/index.ts
Michael Sukkarieh 3b36ffa17e
Add support for structured logs (#323)
* wip on refactoring docs

* wip

* initial structured logs impl

* structured log docs

* create logger package

* add news entry for structured logging

* add logger package to dockerfile and cleanup

* add gh workflow for catching broken links

* further wip

* fix

* further wip on docs

* review feedback

* remove logger dep from mcp package

* fix build errors

* add back auth_url warning

* fix sidebar title consistency

---------

Co-authored-by: bkellam <bshizzle1234@gmail.com>
2025-06-02 11:16:01 -07:00

87 lines
No EOL
2.8 KiB
TypeScript

import winston, { format } from 'winston';
import { Logtail } from '@logtail/node';
import { LogtailTransport } from '@logtail/winston';
import { MESSAGE } from 'triple-beam';
import { env } from './env.js';
/**
* Logger configuration with support for structured JSON logging.
*
* When SOURCEBOT_STRUCTURED_LOGGING_ENABLED=true:
* - Console output will be in JSON format suitable for Datadog ingestion
* - Logs will include structured fields: timestamp, level, message, label, stack (if error)
*
* When SOURCEBOT_STRUCTURED_LOGGING_ENABLED=false (default):
* - Console output will be human-readable with colors
* - Logs will be formatted as: "timestamp level: [label] message"
*/
const { combine, colorize, timestamp, prettyPrint, errors, printf, label: labelFn, json } = format;
const datadogFormat = format((info) => {
info.status = info.level.toLowerCase();
info.service = info.label;
info.label = undefined;
const msg = info[MESSAGE as unknown as string] as string | undefined;
if (msg) {
info.message = msg;
info[MESSAGE as unknown as string] = undefined;
}
return info;
});
const humanReadableFormat = printf(({ level, message, timestamp, stack, label: _label }) => {
const label = `[${_label}] `;
if (stack) {
return `${timestamp} ${level}: ${label}${message}\n${stack}`;
}
return `${timestamp} ${level}: ${label}${message}`;
});
const createLogger = (label: string) => {
const isStructuredLoggingEnabled = env.SOURCEBOT_STRUCTURED_LOGGING_ENABLED === 'true';
return winston.createLogger({
level: env.SOURCEBOT_LOG_LEVEL,
format: combine(
errors({ stack: true }),
timestamp(),
labelFn({ label: label })
),
transports: [
new winston.transports.Console({
format: isStructuredLoggingEnabled
? combine(
datadogFormat(),
json()
)
: combine(
colorize(),
humanReadableFormat
),
}),
...(env.SOURCEBOT_STRUCTURED_LOGGING_FILE && isStructuredLoggingEnabled ? [
new winston.transports.File({
filename: env.SOURCEBOT_STRUCTURED_LOGGING_FILE,
format: combine(
datadogFormat(),
json()
),
}),
] : []),
...(env.LOGTAIL_TOKEN && env.LOGTAIL_HOST ? [
new LogtailTransport(
new Logtail(env.LOGTAIL_TOKEN, {
endpoint: env.LOGTAIL_HOST,
})
)
] : []),
]
});
}
export {
createLogger
};