Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tpv2 logging overhaul #7673

Merged
merged 26 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f9c49f3
Add a logging class
ocket8888 Jul 24, 2023
efd1f45
re-name things to include a Log prefix
ocket8888 Jul 24, 2023
0be1d0b
Add middleware for logging and error handling
ocket8888 Jul 24, 2023
aeaf017
Move file compression stuff into middleware
ocket8888 Jul 24, 2023
32026e3
rework TO proxy handler to use a logger
ocket8888 Jul 24, 2023
f9eb440
Add a front-end logging service
ocket8888 Jul 25, 2023
e2f8dac
fixup allowed console methods now that there's a logger
ocket8888 Jul 25, 2023
63e3f4e
Fix linting errors arising from not using loggers
ocket8888 Jul 25, 2023
44c08b2
Remove console calls from tests
ocket8888 Jul 25, 2023
e827fa0
fix non-camelCase component naming
ocket8888 Jul 25, 2023
6354b65
Simplify type guards in server configuration
ocket8888 Jul 25, 2023
d5e213b
Add typing for a Node SystemError
ocket8888 Aug 1, 2023
7995216
Fix incorrect use of logging middleware
ocket8888 Aug 2, 2023
e25b6d1
Try to clean up some errors being hit in the SSR handler
ocket8888 Aug 2, 2023
04598a3
fix directory handle leak
ocket8888 Aug 2, 2023
be2ebb3
Make non-production server builds debug-able
ocket8888 Aug 2, 2023
1eb8a8b
add initial debug log for all requests, fix double-writing error resp…
ocket8888 Aug 2, 2023
0516afe
Move "time elapsed" debug message so that non-error responses also lo…
ocket8888 Aug 2, 2023
885646a
Remove duplicated check, extraneous substring argument
ocket8888 Aug 2, 2023
b656ae6
Fix missing `req` option to SSR engine handler
ocket8888 Aug 2, 2023
317e16e
Update a lot of fs access to be asynchronous
ocket8888 Aug 2, 2023
9c12452
Fix hard-coded VERSION file path
ocket8888 Aug 2, 2023
7448434
Cache file compressions instead of recalculating on every request
ocket8888 Aug 2, 2023
b7f9797
Fix lint errors from rebasing
ocket8888 Aug 3, 2023
2ea9260
Make regexp non-polynomial
ocket8888 Aug 9, 2023
4cef64e
Fix comment grammar
ocket8888 Aug 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions experimental/traffic-portal/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@
"error",
{
"allow": [
"log",
"warn",
"trace",
"dir",
"timeLog",
"assert",
Expand All @@ -107,8 +106,6 @@
"group",
"groupEnd",
"table",
"dirxml",
"error",
"groupCollapsed",
"Console",
"profile",
Expand Down
9 changes: 6 additions & 3 deletions experimental/traffic-portal/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@
"options": {
"outputPath": "dist/traffic-portal/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json"
"tsConfig": "tsconfig.server.json",
"sourceMap": true,
"buildOptimizer": false,
"optimization": false
},
"configurations": {
"production": {
Expand All @@ -145,8 +148,8 @@
}
],
"sourceMap": false,
"optimization": true,
"buildOptimizer": true
"optimization": true,
"buildOptimizer": true
}
}
},
Expand Down
181 changes: 181 additions & 0 deletions experimental/traffic-portal/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* @license Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { opendir } from "fs/promises";
import { join } from "path";

import type { NextFunction, Request, Response } from "express";

import { LogLevel, Logger } from "src/app/utils";
import { environment } from "src/environments/environment";

import type { ServerConfig } from "./server.config";

/**
* StaticFile defines what compression files are available.
*/
interface StaticFile {
compressions: Array<CompressionType>;
}

/**
* CompressionType defines the different compression algorithms.
*/
interface CompressionType {
fileExt: string;
headerEncoding: string;
name: string;
}

/**
* TPResponseLocals are the express.Response.locals properties specific to a
* response writer for the TP server.
*/
interface TPResponseLocals {
config: ServerConfig;
foundFiles: Map<string, StaticFile>;
logger: Logger;
/** The time at which the request was received. */
startTime: Date;
/**
* The time at which the response was finished being written (or
* `undefined` if not done yet).
*/
endTime?: Date | undefined;
}

/**
* AuthenticatedResponse is a response writer for endpoints that require
* authentication.
*/
export type TPResponseWriter = Response<unknown, TPResponseLocals>;

/**
* An HTTP request handler for the TP server.
*/
export type TPHandler = (req: Request, resp: TPResponseWriter, next: NextFunction) => void | PromiseLike<void>;

const gzip = {
fileExt: "gz",
headerEncoding: "gzip",
name: "gzip"
};
const br = {
fileExt: "br",
headerEncoding: "br",
name: "brotli"
};

/**
* getFiles recursively gets all the files in a directory.
*
* @param path The path to get files from.
* @returns Files found in the directory.
*/
async function getFiles(path: string): Promise<string[]> {
const dir = await opendir(path);
let dirEnt = await dir.read();
let files = new Array<string>();

while (dirEnt !== null) {
const name = join(path, dirEnt.name);

if (dirEnt.isDirectory()) {
files = files.concat(await getFiles(name));
} else {
files.push(name);
}

dirEnt = await dir.read();
}
await dir.close();

return files;
}

/**
* loggingMiddleWare is a middleware factory for express.js that provides a
* logger.
* It does also provide a link to server configuration that can be used in
* handlers, and a couple other niceties.
*
* @param config The server configuration.
* @returns A middleware that adds a property `logger` to `resp.locals` for
* logging purposes.
*/
export async function loggingMiddleWare(config: ServerConfig): Promise<TPHandler> {
const allFiles = await getFiles(config.browserFolder);
const compressedFiles = new Map(
allFiles.filter(
file => file.match(/\.(br|gz)$/)
).map(
file => [file, undefined]
)
);
const foundFiles = new Map<string, StaticFile>(
allFiles.filter(
file => file.match(/\.(js|css|tff|svg)$/)
).map(
file => {
const staticFile: StaticFile = {
compressions: []
};
if (compressedFiles.has(`${file}.${br.fileExt}`)) {
staticFile.compressions.push(br);
}
if (compressedFiles.has(`${file}.${gzip.fileExt}`)) {
staticFile.compressions.push(gzip);
}
return [file, staticFile];
}
)
);

return async (req: Request, resp: TPResponseWriter, next: NextFunction): Promise<void> => {
resp.locals.config = config;
const prefix = `${req.ip} HTTP/${req.httpVersion} ${req.method} ${req.url} ${req.hostname}`;
resp.locals.logger = new Logger(console, environment.production ? LogLevel.INFO : LogLevel.DEBUG, prefix);
resp.locals.logger.debug("handling");
resp.locals.startTime = new Date();
resp.locals.foundFiles = foundFiles;

next();
};
}

/**
* errorMiddleWare is a middleware for express.js that provides automatic
* handling of errors that aren't caught in the endpoint handlers.
*
* @param err Any error passed along by other handlers.
* @param _ The client request - unused.
* @param resp The server's response-writer
* @param next A function provided by Express which will call the next handler.
*/
export function errorMiddleWare(err: unknown, _: Request, resp: TPResponseWriter, next: NextFunction): void {
if (err !== null && err !== undefined) {
resp.locals.logger.error("unhandled error bubbled to routing:", String(err));
if (!environment.production) {
console.trace(err);
}
if (!resp.locals.endTime) {
resp.status(502); // "Bad Gateway"
resp.write('{"alerts":[{"level":"error","text":"Unknown Traffic Portal server error occurred"}]}\n');
resp.end("\n");
resp.locals.endTime = new Date();
next(err);
}
}
}
3 changes: 2 additions & 1 deletion experimental/traffic-portal/nightwatch/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"no-restricted-imports": [
"error",
"../"
]
],
"no-console": "off"
}
}
]
Expand Down
Loading
Loading