Skip to content

Commit a8f8181

Browse files
committed
Switch to pino logger and add Loki backend
1 parent ed9125a commit a8f8181

10 files changed

Lines changed: 118 additions & 74 deletions

File tree

bun.lock

Lines changed: 50 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020
"typescript": "^5"
2121
},
2222
"dependencies": {
23+
"@bogeychan/elysia-logger": "^0.1.10",
2324
"@elysiajs/html": "^1.4.0",
2425
"@keyv/redis": "^5.1.6",
2526
"elysia": "^1.4.26",
2627
"keyv": "^5.6.0",
2728
"keyv-file": "^5.3.3",
29+
"pino": "^10.3.1",
30+
"pino-loki": "^3.0.0",
31+
"pino-pretty": "^13.1.3",
2832
"prom-client": "^15.1.3",
29-
"tle": "^3.0.1",
30-
"winston": "^3.19.0"
33+
"tle": "^3.0.1"
3134
}
3235
}

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Elysia } from "elysia";
22
import { html } from "@elysiajs/html";
3+
import { wrap } from "@bogeychan/elysia-logger";
34

45
import tleRoute from "./routes/tle";
56
import noradRoute from "./routes/norad";
@@ -10,6 +11,7 @@ import config from "./utils/config";
1011
import log from "./utils/logger";
1112

1213
new Elysia()
14+
.use(wrap(log))
1315
// Use HTML plugin for rendering the index page
1416
.use(html())
1517
.get("/styles.css", () => new Response(Bun.file(new URL("./pub/styles.css", import.meta.url)), { headers: { "Content-Type": "text/css" } }))

src/routes/tle.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,26 @@ const tleRoute = new Elysia({ prefix: "/tle" })
2424
const lastFetchedHeader = new Date(ctx.request.headers.get("If-Modified-Since") || 0).getTime();
2525

2626
if (lastFetchedHeader && timestamp && lastFetchedHeader <= timestamp && !isStale) {
27-
log.info(`TLEs for group "${group}" not modified since last fetch. Returning 304.`);
27+
log.debug(`TLEs for group "${group}" not modified since last fetch. Returning 304.`);
2828
return new Response(null, { status: 304, headers: { "Last-Modified": new Date(timestamp).toUTCString(), "Cache-Control": `max-age=${group === "active" ? Math.ceil((config.cacheActiveDuration - (now - timestamp)) / 1000) : Math.ceil((config.cacheDuration - (now - timestamp)) / 1000)}` } });
2929
}
3030

3131
let tle = await kv.get(group);
3232

3333
if (!tle) {
34-
log.info(`No cached TLEs for group "${group}". Fetching from Celestrak...`);
34+
log.debug(`No cached TLEs for group "${group}". Fetching from Celestrak...`);
3535
tle = await tleFetcher(group);
3636
timestamp = now;
3737
kv.set(group, tle);
3838
kv.set(`${group}_timestamp`, timestamp);
3939
} else if (isStale) {
40-
log.info(`TLEs for group "${group}" are stale. Fetching fresh TLEs...`);
40+
log.debug(`TLEs for group "${group}" are stale. Fetching fresh TLEs...`);
4141
tle = await tleFetcher(group);
4242
timestamp = now;
4343
kv.set(group, tle);
4444
kv.set(`${group}_timestamp`, timestamp);
4545
} else {
46-
log.info(`Serving cached TLEs for group "${group}".`);
46+
log.debug(`Serving cached TLEs for group "${group}".`);
4747
}
4848

4949
return new Response(tle, {

src/utils/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ const config = {
22
allowedGroups: process.env.ALLOWED_GROUPS ? process.env.ALLOWED_GROUPS.split(",").concat("active") : ["active"],
33
logLevel: process.env.LOG_LEVEL || "info",
44
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
5+
redisUri: process.env.REDIS_URI || null,
6+
kvFile: process.env.KV_FILE || "./data/kv.json",
7+
lokiUri: process.env.LOKI_URI || null,
8+
lokiAuth: process.env.LOKI_AUTH || undefined,
59
rateLimitWindow: process.env.RATE_LIMIT_WINDOW ? parseInt(process.env.RATE_LIMIT_WINDOW) * 1000 : 60 * 1000,
610
rateLimitMaxRequests: process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 60,
711
cacheDuration: process.env.CACHE_DURATION ? parseInt(process.env.CACHE_DURATION) * 1000 : 24 * 60 * 60 * 1000,

src/utils/kv.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,33 @@ import Keyv from "keyv";
22
import { KeyvFile } from "keyv-file";
33
import KeyvRedis from "@keyv/redis";
44

5+
import config from "./config";
56
import log from "./logger";
67

78
let kv: Keyv<any>;
89

9-
if (process.env.REDIS_URI) {
10+
if (config.redisUri) {
1011
log.info("Using Redis for key-value storage.");
1112

1213
kv = new Keyv({
13-
store: new KeyvRedis(process.env.REDIS_URL),
14+
store: new KeyvRedis(config.redisUri),
1415
});
1516

1617
kv.on("error", (err) => {
17-
log.error(`Keyv Redis error: ${err}`);
18+
log.child(err).error("Keyv Redis error:");
1819
});
1920
} else {
2021
log.info("Using file-based storage for key-value storage.");
2122

2223
kv = new Keyv({
2324
store: new KeyvFile({
24-
filename: process.env.KV_FILE || "./data/kv.json",
25+
filename: config.kvFile,
2526
writeDelay: 100,
2627
}),
2728
});
2829

2930
kv.on("error", (err) => {
30-
log.error(`Keyv File error: ${err}`);
31+
log.child(err).error("Keyv File error:");
3132
});
3233
}
3334

src/utils/logger.ts

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1-
import winston from "winston";
1+
import pino from "pino";
22

33
import config from "./config";
44

5-
const log = winston.createLogger({
6-
level: config.logLevel,
7-
format: winston.format.combine(
8-
winston.format.timestamp(),
9-
winston.format.colorize(),
10-
winston.format.printf(({ timestamp, level, message }) => {
11-
return `${timestamp} [${level}]: ${message}`;
12-
}),
13-
),
14-
transports: [new winston.transports.Console()],
15-
});
5+
let transport: pino.DestinationStream;
6+
7+
if (config.lokiUri) {
8+
transport = pino.transport({
9+
target: "pino-loki",
10+
options: {
11+
host: config.lokiUri,
12+
basicAuth: config.lokiAuth,
13+
labels: { app: "retlector" },
14+
json: true,
15+
},
16+
});
17+
} else {
18+
transport = pino.transport({
19+
target: "pino-pretty",
20+
options: {
21+
colorize: true,
22+
translateTime: "yyyy-mm-dd HH:MM:ss.l o",
23+
ignore: "pid,hostname",
24+
},
25+
});
26+
}
27+
28+
const log = pino({ level: config.logLevel }, transport);
29+
30+
if (config.lokiUri) {
31+
log.info("Logging to Loki at " + config.lokiUri);
32+
} else {
33+
log.info("Logging to console");
34+
}
1635

1736
export default log;

src/utils/ratelimiter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const limiter = new Elysia({ name: "rate-limiter" }).onRequest(async (ctx) => {
2323
if (now - clientInfo.startTime < windowSize) {
2424
clientInfo.count++;
2525
if (clientInfo.count > maxRequests) {
26-
log.info(`Rate limit exceeded for IP ${ip}`);
26+
log.debug(`Rate limit exceeded for IP ${ip}`);
2727
return new Response("Too Many Requests", {
2828
status: 429,
2929
headers: {

src/utils/tleFetcher.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { version } from "../../package.json";
55

66
async function fetchTle(group: string) {
77
const url = `https://celestrak.org/NORAD/elements/gp.php?GROUP=${group}&FORMAT=tle`;
8-
log.info(`Fetching TLEs for group "${group}" from Celestrak...`);
8+
log.debug(`Fetching TLEs for group "${group}" from Celestrak...`);
99
try {
1010
const lastFetch = await kv.get(`${group}_timestamp`);
1111
const response = await fetch(url, {
@@ -20,10 +20,14 @@ async function fetchTle(group: string) {
2020
const tleData = await response.text();
2121
await kv.set(group, tleData);
2222
await kv.set(`${group}_timestamp`, Date.now());
23-
log.info(`Successfully cached TLEs for group "${group}".`);
23+
log.debug(`Successfully cached TLEs for group "${group}".`);
2424
return tleData;
2525
} catch (error) {
26-
log.error(`Error fetching TLEs for group "${group}": ${error}`);
26+
if (error instanceof Error) {
27+
log.child(error).error(`Error fetching TLEs for group "${group}":`);
28+
} else {
29+
log.error(`Error fetching TLEs for group "${group}": ${error}`);
30+
}
2731
throw error;
2832
}
2933
}

src/utils/tleGetter.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ async function getObjectsTle(noradId: number) {
4545

4646
// If we're here then active group doesn't contain the requested NORAD ID, try fetching the TLE directly from Celestrak, but be aware of rate limits so we don't get blocked
4747
if (!tleData) {
48-
log.verbose(`NORAD ID ${noradId} not found in active group. Attempting to fetch directly from Celestrak...`);
48+
log.debug(`NORAD ID ${noradId} not found in active group. Attempting to fetch directly from Celestrak...`);
4949
const url = `https://celestrak.org/NORAD/elements/gp.php?CATNR=${noradId}&FORMAT=tle`;
5050
try {
5151
let tries: number = (await kv.get(`celestrakTries`)) || 0;
@@ -71,17 +71,21 @@ async function getObjectsTle(noradId: number) {
7171
});
7272

7373
if (!response.ok) {
74-
log.error(`Failed to fetch TLE from Celestrak: ${response.status} ${response.statusText}`);
74+
log.child({ status: response.status, statusText: response.statusText }).error(`Failed to fetch TLE for NORAD ID ${noradId} from Celestrak`);
7575
throw new Error(`Failed to fetch TLE from Celestrak`);
7676
}
7777

7878
tleData = (await response.text()) as string;
7979
await kv.set(`tle_${noradId}`, tleData);
8080
await kv.set(`celestrakTries`, tries + 1);
8181
await kv.set(`celestrakLastTry`, Date.now());
82-
log.info(`Successfully fetched TLE for NORAD ID ${noradId} from Celestrak.`);
82+
log.debug(`Successfully fetched TLE for NORAD ID ${noradId} from Celestrak.`);
8383
} catch (error) {
84-
log.error(`Error fetching TLE for NORAD ID ${noradId}: ${error}`);
84+
if (error instanceof Error) {
85+
log.child(error).error(`Error fetching TLE for NORAD ID ${noradId}:`);
86+
} else {
87+
log.error(`Error fetching TLE for NORAD ID ${noradId}: ${error}`);
88+
}
8589
throw error;
8690
}
8791
}

0 commit comments

Comments
 (0)