logging.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import * as winston from "winston";
  2. import * as nodemailer from "nodemailer";
  3. import TransportStream from "winston-transport";
  4. import Mail from "nodemailer/lib/mailer";
  5. import { hasStackTrace } from "./async_utils";
  6. import { inspect } from "util";
  7. export function createLogger(opts?: {
  8. errorHandler?: (reason: unknown) => Error | undefined
  9. }) {
  10. process.on("unhandledRejection", (reason) => {
  11. if (opts?.errorHandler) {
  12. const c = opts.errorHandler(reason);
  13. if (c !== undefined)
  14. throw c;
  15. }
  16. if (hasStackTrace(reason))
  17. throw new Error(`Unhandled rejection: ${reason}\nFull stack trace: ${reason.stack}`);
  18. let contents = `${reason}`;
  19. try {
  20. contents = inspect(reason, true, null);
  21. } catch (e) {
  22. // ignored
  23. }
  24. throw new Error(`Unhandled rejection: ${contents}`);
  25. });
  26. const logger = winston.createLogger({
  27. level: "debug",
  28. transports: [
  29. new winston.transports.Console({
  30. handleExceptions: true,
  31. level: "debug",
  32. debugStdout: true,
  33. format: winston.format.combine(
  34. winston.format.splat(),
  35. winston.format.prettyPrint(),
  36. winston.format.cli()
  37. ),
  38. }),
  39. ]
  40. });
  41. if (process.env.NODE_ENV == "production" && process.env.GMAIL_NAME !== undefined && process.env.GMAIL_PASSWORD !== undefined) {
  42. logger.add(new EmailTransport({
  43. level: "error",
  44. handleExceptions: true,
  45. format: winston.format.combine(
  46. winston.format.splat(),
  47. winston.format.simple()
  48. )
  49. }));
  50. }
  51. return logger;
  52. }
  53. interface LogMessage {
  54. message: string;
  55. }
  56. class EmailTransport extends TransportStream {
  57. private mailer: Mail;
  58. constructor(opts?: TransportStream.TransportStreamOptions) {
  59. super(opts);
  60. this.mailer = nodemailer.createTransport({
  61. host: "smtp.gmail.com",
  62. port: 465,
  63. secure: true,
  64. auth: {
  65. user: process.env.GMAIL_NAME,
  66. pass: process.env.GMAIL_PASSWORD
  67. }
  68. });
  69. }
  70. log(info: LogMessage, next: () => void): void {
  71. setImmediate(() => {
  72. this.emit("logged", info);
  73. });
  74. this.mailer.sendMail({
  75. from: `${process.env.GMAIL_NAME}@gmail.com`,
  76. to: process.env.ERRORS_ADDR,
  77. subject: `Error at ${new Date().toISOString()}`,
  78. text: `Received error data: ${info.message}`
  79. }).catch(err => console.log(`Failed to send email! ${err}`));
  80. next();
  81. }
  82. }