Skip to content

Commit 540816d

Browse files
authored
add logger options
1 parent c23792b commit 540816d

File tree

3 files changed

+98
-25
lines changed

3 files changed

+98
-25
lines changed

example/server.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ app.prepare().then(() => {
2121
salt: '72456c286761260f320391fe500fcec53755958dabd288867a6db072e1bc1dbd84b15079838a83a715edc1ecad50c3ce91dd8fdef6f981816fa274f91d8ecf06',
2222
},
2323
bucketWhitelist: ['test-bucket'],
24+
logging: {
25+
level: 'debug',
26+
},
2427
});
2528
} else {
2629
handle(req, res, parsedUrl);

src/index.tsx

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,21 @@ import { ServerResponse, request as httpRequest } from 'http';
77

88
import pb from '@bitpatty/imgproxy-url-builder';
99

10-
const IMGPROXY_ENDPOINT = '/_next/imgproxy';
10+
import Logger, { LoggerOptions } from './logger';
1111

12-
const SRC_REGEX = /^[^/.]+\/.+[^/]$/;
12+
type HandlerOptions = {
13+
signature?: {
14+
key: string;
15+
salt: string;
16+
};
17+
authToken?: string;
18+
bucketWhitelist?: string[];
19+
forwardedHeaders?: string[];
20+
logging?: LoggerOptions;
21+
};
1322

23+
const IMGPROXY_ENDPOINT = '/_next/imgproxy';
24+
const SRC_REGEX = /^[^/.]+\/.+[^/]$/;
1425
const FORWARDED_HEADERS = [
1526
'date',
1627
'expires',
@@ -22,8 +33,7 @@ const FORWARDED_HEADERS = [
2233
];
2334

2435
/**
25-
* Builds the final reuest path to retrieve
26-
* an image from the imgproxy instance
36+
* Builds the final reuest path to retrieve an image from the imgproxy instance
2737
*
2838
* @param src The source file
2939
* @param options The imgproxy options
@@ -53,16 +63,6 @@ const buildRequestPath = (
5363
});
5464
};
5565

56-
type ImageOptimizerOptions = {
57-
signature?: {
58-
key: string;
59-
salt: string;
60-
};
61-
authToken?: string;
62-
bucketWhitelist?: string[];
63-
forwardedHeaders?: string[];
64-
};
65-
6666
/**
6767
* Handles an image request
6868
*
@@ -75,22 +75,31 @@ const handle = (
7575
imgproxyBaseUrl: URL,
7676
query: ParsedUrlQuery,
7777
res: ServerResponse,
78-
options?: ImageOptimizerOptions,
78+
options?: HandlerOptions,
7979
): void => {
8080
const { src, params } = query;
81+
Logger.debug(options?.logging, 'Processing query', { src, params });
82+
8183
const { authToken, bucketWhitelist, forwardedHeaders, signature } =
8284
options ?? {};
8385

84-
// If the source is not set of fails the
85-
// regex check throw a 400
86+
// If the source is not set of fails the regex check throw a 400
8687
if (!src || Array.isArray(src) || !SRC_REGEX.test(src)) {
88+
Logger.error(options?.logging, 'Source failed validation check', src);
8789
res.statusCode = 400;
8890
res.end();
8991
return;
9092
}
9193

92-
// If the bucket whitelist is set throw a 400
93-
if (bucketWhitelist && !bucketWhitelist.includes(src.split('/')[0])) {
94+
// If the bucket whitelist is set throw a 400 in case the bucket is
95+
// not included
96+
const bucketName = src.split('/')[0];
97+
if (bucketWhitelist && !bucketWhitelist.includes(bucketName)) {
98+
Logger.error(
99+
options?.logging,
100+
'Requested bucket is not whitelisted',
101+
bucketName,
102+
);
94103
res.statusCode = 400;
95104
res.end();
96105
return;
@@ -101,11 +110,13 @@ const handle = (
101110
signature,
102111
});
103112

104-
const reqMethod = imgproxyBaseUrl.protocol.startsWith('https')
113+
Logger.debug(options?.logging, 'Built imgproxy URL', requestPath);
114+
115+
const reqProto = imgproxyBaseUrl.protocol.startsWith('https')
105116
? httpsRequest
106117
: httpRequest;
107118

108-
const req = reqMethod(
119+
const req = reqProto(
109120
{
110121
hostname: imgproxyBaseUrl.hostname,
111122
...(imgproxyBaseUrl.port ? { port: imgproxyBaseUrl.port } : {}),
@@ -117,18 +128,33 @@ const handle = (
117128
},
118129
(r) => {
119130
(forwardedHeaders ?? FORWARDED_HEADERS).forEach((h) => {
120-
if (r.headers[h]) res.setHeader(h, r.headers[h] as string);
131+
if (r.headers[h]) {
132+
Logger.debug(options?.logging, 'Forwarding header', {
133+
requestPath,
134+
hheader: h,
135+
});
136+
res.setHeader(h, r.headers[h] as string);
137+
}
121138
});
122139

123-
if (r.statusCode) res.statusCode = r.statusCode;
140+
if (r.statusCode) {
141+
Logger.debug(options?.logging, 'Received status code', {
142+
requestPath,
143+
statusCode: r.statusCode,
144+
});
145+
res.statusCode = r.statusCode;
146+
}
124147

125148
r.pipe(res);
126-
r.on('end', () => res.end());
149+
r.on('end', () => {
150+
Logger.debug(options?.logging, 'Stream ended', { requestPath });
151+
res.end();
152+
});
127153
},
128154
);
129155

130156
req.on('error', (e) => {
131-
console.error(e);
157+
Logger.error(options?.logging, 'Stream error', { requestPath, error: e });
132158
res.statusCode = 500;
133159
res.end();
134160
req.destroy();

src/logger.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
enum LOG_LEVEL {
2+
debug = 2,
3+
warn = 4,
4+
error = 5,
5+
}
6+
7+
type LoggerOptions = {
8+
logger?: Pick<typeof console, 'debug' | 'warn' | 'error'>;
9+
level?: keyof LOG_LEVEL;
10+
};
11+
12+
type LogFunc = (
13+
opts: LoggerOptions | undefined,
14+
msg: string,
15+
...args: unknown[]
16+
) => void;
17+
18+
class Logger {
19+
static #shouldLog(opts: LoggerOptions | undefined, lvl: LOG_LEVEL): boolean {
20+
return LOG_LEVEL[opts?.level ?? 'error'] <= lvl;
21+
}
22+
23+
static #log(
24+
lvl: keyof typeof LOG_LEVEL,
25+
opts: LoggerOptions | undefined,
26+
msg: string,
27+
...args: unknown[]
28+
) {
29+
if (!this.#shouldLog(opts, LOG_LEVEL[lvl])) return;
30+
(opts?.logger ?? console)[lvl](msg, ...args);
31+
}
32+
33+
public static readonly warn: LogFunc = (...args) =>
34+
this.#log('warn', ...args);
35+
36+
public static readonly debug: LogFunc = (...args) =>
37+
this.#log('debug', ...args);
38+
39+
public static readonly error: LogFunc = (...args) =>
40+
this.#log('error', ...args);
41+
}
42+
43+
export default Logger;
44+
export { LoggerOptions };

0 commit comments

Comments
 (0)