implemented new logging system in first iteration

This commit is contained in:
2025-12-31 11:26:44 -05:00
parent 46988f1921
commit d101bf9686
9 changed files with 56 additions and 49 deletions

View File

@@ -24,7 +24,7 @@ APPLICATION_ENVIRONMENT= # dev / prod
CONFIG_ID= # configures CONFIG_ID= # configures
# Logger # Logger
LOG_DEPTH= # normal: 0, verbose: 1 LOG_DEPTH= # normal / verbose
# Glitchtip # Glitchtip
GLITCHTIP_DSN= GLITCHTIP_DSN=

View File

@@ -1,8 +1,5 @@
// const mariadb = require('mariadb') // const mariadb = require('mariadb')
import * as mariadb from 'mariadb'; import * as mariadb from 'mariadb';
const dotenv = require('dotenv')
dotenv.config();
const pool = mariadb.createPool({ const pool = mariadb.createPool({
host: process.env.DB_HOST, host: process.env.DB_HOST,

View File

@@ -1,9 +1,11 @@
import dotenv = require('dotenv'); import dotenv = require('dotenv');
dotenv.config(); dotenv.config({ quiet: true });
import express = require('express'); import express = require('express');
import cors = require('cors'); import cors = require('cors');
import morgan = require('morgan'); import morgan = require('morgan');
import { logger, LogHeader, LogPayload } from './services/logging/logger';
const app = express() const app = express()
app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express.Response) => { app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express.Response) => {
@@ -29,26 +31,10 @@ app.use(morgan((tokens: morgan.TokenIndexer, req: express.Request, res: express.
} }
logger.log(head.level, head.type, payload.message, payload.data, head.depth) logger.log(head.level, head.type, payload.message, payload.data, head.depth)
// {
// type: 'http',
// timestamp: new Date().toISOString(),
// method: tokens.method(req, res),
// path: tokens.url(req, res),
// status: Number(tokens.status(req, res)),
// response_time_ms: Number(tokens['response-time'](req, res)),
// ip: req.ip,
// user_agent: req.headers['user-agent'],
// user: req.user
// ? { id: req.user.id, name: req.user.name }
// : null,
// }
return ''; return '';
}, { }, {
skip: (req: express.Request) => { skip: (req: express.Request) => {
return req.originalUrl === '/members/me'; return req.originalUrl === '/members/me' || req.originalUrl === '/ping';
} }
})) }))
@@ -66,14 +52,13 @@ const port = process.env.SERVER_PORT;
//glitchtip setup //glitchtip setup
import sentry = require('@sentry/node'); import sentry = require('@sentry/node');
if (process.env.DISABLE_GLITCHTIP === "true") { if (process.env.DISABLE_GLITCHTIP === "true") {
console.log("Glitchtip disabled") logger.info('app', 'Glitchtip disabled', null, 'normal')
} else { } else {
let dsn = process.env.GLITCHTIP_DSN; let dsn = process.env.GLITCHTIP_DSN;
let release = process.env.APPLICATION_VERSION; let release = process.env.APPLICATION_VERSION;
let environment = process.env.APPLICATION_ENVIRONMENT; let environment = process.env.APPLICATION_ENVIRONMENT;
console.log(release, environment)
sentry.init({ dsn: dsn, release: release, environment: environment, integrations: [sentry.captureConsoleIntegration({ levels: ['error'] })] }); sentry.init({ dsn: dsn, release: release, environment: environment, integrations: [sentry.captureConsoleIntegration({ levels: ['error'] })] });
console.log("Glitchtip initialized"); logger.info('app', 'Glitchtip initialized', null, 'normal')
} }
//session setup //session setup
@@ -112,7 +97,6 @@ import { roles, memberRoles } from './routes/roles';
import { courseRouter, eventRouter } from './routes/course'; import { courseRouter, eventRouter } from './routes/course';
import { calendarRouter } from './routes/calendar'; import { calendarRouter } from './routes/calendar';
import { docsRouter } from './routes/docs'; import { docsRouter } from './routes/docs';
import { logger, LogHeader, LogPayload } from './services/logging/logger';
app.use('/application', applicationRouter); app.use('/application', applicationRouter);
app.use('/ranks', ranks); app.use('/ranks', ranks);
@@ -134,5 +118,5 @@ app.get('/ping', (req, res) => {
}); });
app.listen(port, () => { app.listen(port, () => {
console.log(`Example app listening on port ${port} `) logger.info('app', `Example app listening on port ${port} `)
}) })

View File

@@ -1,7 +1,5 @@
const passport = require('passport'); const passport = require('passport');
const OpenIDConnectStrategy = require('passport-openidconnect'); const OpenIDConnectStrategy = require('passport-openidconnect');
const dotenv = require('dotenv');
dotenv.config();
const express = require('express'); const express = require('express');
const { param } = require('./applications'); const { param } = require('./applications');
@@ -13,6 +11,7 @@ import { getUserRoles } from '../services/db/rolesService';
import { getUserState, mapDiscordtoID } from '../services/db/memberService'; import { getUserState, mapDiscordtoID } from '../services/db/memberService';
import { MemberState } from '@app/shared/types/member'; import { MemberState } from '@app/shared/types/member';
import { toDateTime } from '@app/shared/utils/time'; import { toDateTime } from '@app/shared/utils/time';
import { logger } from '../services/logging/logger';
const querystring = require('querystring'); const querystring = require('querystring');
function parseJwt(token) { function parseJwt(token) {
@@ -37,10 +36,10 @@ passport.use(new OpenIDConnectStrategy({
// console.log('profile:', profile); // console.log('profile:', profile);
// console.log('jwt: ', parseJwt(jwtClaims)); // console.log('jwt: ', parseJwt(jwtClaims));
// console.log('params:', params); // console.log('params:', params);
let con;
try { try {
var con = await pool.getConnection(); con = await pool.getConnection();
await con.beginTransaction(); await con.beginTransaction();
@@ -49,7 +48,13 @@ passport.use(new OpenIDConnectStrategy({
let memberId: number | null = null; let memberId: number | null = null;
//if member exists //if member exists
if (existing.length > 0) { if (existing.length > 0) {
//login
memberId = existing[0].id; memberId = existing[0].id;
logger.info('auth', `Existing member login`, {
memberId,
issuer,
});
} else { } else {
//otherwise: create account mode //otherwise: create account mode
const jwt = parseJwt(jwtClaims); const jwt = parseJwt(jwtClaims);
@@ -61,11 +66,16 @@ passport.use(new OpenIDConnectStrategy({
if (discordID && memberId) { if (discordID && memberId) {
// claim account // claim account
console.log("Claiming account");
const result = await con.query( const result = await con.query(
`UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`, `UPDATE members SET authentik_sub = ?, authentik_issuer = ? WHERE id = ?;`,
[sub, issuer, memberId] [sub, issuer, memberId]
) )
logger.info('auth', `Existing member claimed via Discord`, {
memberId,
discordID,
issuer,
});
} else { } else {
console.log("New Account"); console.log("New Account");
// new account // new account
@@ -75,6 +85,13 @@ passport.use(new OpenIDConnectStrategy({
[username, sub, issuer] [username, sub, issuer]
) )
memberId = Number(result.insertId); memberId = Number(result.insertId);
logger.info('auth', `New member account created`, {
memberId,
username,
issuer,
});
} }
} }
@@ -83,10 +100,26 @@ passport.use(new OpenIDConnectStrategy({
await con.commit(); await con.commit();
return cb(null, { memberId }); return cb(null, { memberId });
} catch (error) { } catch (error) {
await con.rollback(); logger.error('auth', `Authentication transaction failed`, {
issuer,
error: error instanceof Error ? error.message : String(error),
stack: error instanceof Error ? error.stack : undefined,
});
if (con) {
try {
await con.rollback();
} catch (rollbackError) {
logger.error('auth', `Rollback failed`, {
error: rollbackError instanceof Error
? rollbackError.message
: String(rollbackError),
});
}
}
return cb(error); return cb(error);
} finally { } finally {
con.release(); if (con) con.release();
} }
})); }));

View File

@@ -30,7 +30,6 @@ ur.post('/', [requireRole(["17th Command", "17th Administrator", "17th HQ"]), re
ur.get('/', async (req: express.Request, res: express.Response) => { ur.get('/', async (req: express.Request, res: express.Response) => {
try { try {
const promos = await getPromotionHistorySummary(); const promos = await getPromotionHistorySummary();
console.log(promos);
res.status(200).json(promos); res.status(200).json(promos);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@@ -44,7 +43,6 @@ ur.get('/:day', async (req: express.Request, res: express.Response) => {
let day = new Date(req.params.day) let day = new Date(req.params.day)
const promos = await getPromotionsOnDay(day); const promos = await getPromotionsOnDay(day);
console.log(promos);
res.status(200).json(promos); res.status(200).json(promos);
} catch (err) { } catch (err) {
console.error(err); console.error(err);

View File

@@ -39,7 +39,6 @@ export async function insertMemberRank(member_id: number, rank_id: number, date?
export async function batchInsertMemberRank(promos: BatchPromotionMember[], author: number) { export async function batchInsertMemberRank(promos: BatchPromotionMember[], author: number) {
try { try {
var con = await pool.getConnection(); var con = await pool.getConnection();
console.log(promos);
promos.forEach(p => { promos.forEach(p => {
con.query(`CALL sp_update_member_rank(?, ?, ?, ?, ?, ?)`, [p.member_id, p.rank_id, author, author, "Rank Change", toDateTime(new Date(p.start_date))]) con.query(`CALL sp_update_member_rank(?, ?, ?, ?, ?, ?)`, [p.member_id, p.rank_id, author, author, "Rank Change", toDateTime(new Date(p.start_date))])
}); });
@@ -70,7 +69,7 @@ export async function getPromotionHistorySummary(page: number = 1, pageSize: num
let promoList: PromotionSummary[] = await pool.query(sql, [pageSize, offset]) as PromotionSummary[]; let promoList: PromotionSummary[] = await pool.query(sql, [pageSize, offset]) as PromotionSummary[];
let loaCount = Number((await pool.query(`SELECT let rowCount = Number((await pool.query(`SELECT
COUNT(*) AS total_grouped_days_count COUNT(*) AS total_grouped_days_count
FROM FROM
( (
@@ -79,10 +78,9 @@ export async function getPromotionHistorySummary(page: number = 1, pageSize: num
WHERE reason = 'Rank Change' WHERE reason = 'Rank Change'
) AS grouped_days;`))[0]); ) AS grouped_days;`))[0]);
console.log(loaCount); let pageCount = rowCount / pageSize;
let pageCount = loaCount / pageSize;
let output: PagedData<PromotionSummary> = { data: promoList, pagination: { page: page, pageSize: pageSize, total: loaCount, totalPages: pageCount } } let output: PagedData<PromotionSummary> = { data: promoList, pagination: { page: page, pageSize: pageSize, total: rowCount, totalPages: pageCount } }
return output; return output;
} }

View File

@@ -1,6 +1,6 @@
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
export type LogDepth = 'normal' | 'verbose'; export type LogDepth = 'normal' | 'verbose';
export type LogType = 'http' | 'app'; export type LogType = 'http' | 'app' | 'auth';
export interface LogHeader { export interface LogHeader {
timestamp: string; timestamp: string;
@@ -27,7 +27,6 @@ function shouldLog(depth: LogDepth) {
function emitLog(header: LogHeader, payload: LogPayload = {}) { function emitLog(header: LogHeader, payload: LogPayload = {}) {
if (!shouldLog(header.depth)) return; if (!shouldLog(header.depth)) return;
console.log(header, payload);
const logLine = { ...header, ...payload }; const logLine = { ...header, ...payload };
console.log(JSON.stringify(logLine)); console.log(JSON.stringify(logLine));
@@ -51,19 +50,19 @@ export const logger = {
emitLog(header, payload); emitLog(header, payload);
}, },
info(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) { info(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
this.log('info', type, message, data, depth, context); this.log('info', type, message, data, depth, context);
}, },
debug(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) { debug(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
this.log('debug', type, message, data, depth, context); this.log('debug', type, message, data, depth, context);
}, },
warn(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) { warn(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
this.log('warn', type, message, data, depth, context); this.log('warn', type, message, data, depth, context);
}, },
error(type: string, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) { error(type: LogType, message: string, data?: Record<string, any>, depth: LogDepth = 'normal', context?: Partial<LogHeader>) {
this.log('error', type, message, data, depth, context); this.log('error', type, message, data, depth, context);
}, },
} }

View File

@@ -61,7 +61,6 @@ export async function getPromoHistory(page?: number, pageSize?: number): Promise
} }
export async function getPromotionsOnDay(day: Date): Promise<PromotionDetails[]> { export async function getPromotionsOnDay(day: Date): Promise<PromotionDetails[]> {
console.log(day.toISOString());
const res = await fetch(`${addr}/memberRanks/${day.toISOString()}`, { const res = await fetch(`${addr}/memberRanks/${day.toISOString()}`, {
credentials: 'include', credentials: 'include',
}) })

View File

@@ -105,7 +105,6 @@ export const useMemberDirectory = defineStore('memberDirectory', () => {
try { try {
const res = await getFullMembers(ids); const res = await getFullMembers(ids);
for (const m of res) { for (const m of res) {
console.log(m)
full[m.member.member_id] = m; full[m.member.member_id] = m;
const waiters = fullWaiters.get(m.member.member_id); const waiters = fullWaiters.get(m.member.member_id);