Skip to content

Commit

Permalink
feat: improve logging with log4js
Browse files Browse the repository at this point in the history
  • Loading branch information
JustSamuel committed Nov 9, 2024
1 parent 095ccfd commit f84d4ac
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 18 deletions.
3 changes: 2 additions & 1 deletion .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ IMAP_TLS=false
IMAP_USERNAME=svc@domain.nl
IMAP_PASSWORD=CHANGEME
PLANKA_API_KEY=CHANGEME
PLANKA_URL=https://plankanban.github.io/planka/#/
PLANKA_URL=https://plankanban.github.io/planka/#/
LOG_LEVEL=info
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ This application requires several environment variables to function correctly. B
| `IMAP_PASSWORD` | The password for the IMAP account. | `undefined` |
| `PLANKA_API_KEY` | The API key for authenticating with the Planka service. | `undefined` |
| `PLANKA_URL` | The base URL of the Planka API. | `http://localhost:3000` |
| `LOG_LEVEL` | The log level for the application. | `info` |

### Note:
Make sure to replace the default values with actual credentials before running the application.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"dependencies": {
"@gewis/planka-client": "github:GEWIS/planka-client#v1.1.0",
"@hey-api/client-fetch": "^0.4.2",
"imapflow": "^1.0.169"
"imapflow": "^1.0.169",
"log4js": "^6.9.1"
},
"devDependencies": {
"@gewis/eslint-config": "https://github.com/GEWIS/eslint-config#v1.2.0",
Expand Down
3 changes: 3 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { config } from 'dotenv';

config();
16 changes: 11 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { config } from 'dotenv';
config();

import './env';
import log4js from 'log4js';
import Mailer from './mailer';
import Planka from './planka';

const logger = log4js.getLogger('Main');
logger.level = process.env['LOG_LEVEL'] || 'info';

// Main workflow
async function main() {
Planka.initialize();
const mailer: Mailer = new Mailer();

try {
logger.info('Starting process...');
const emails = await mailer.handleEmails();
const result = await Planka.processCards(emails);
await mailer.handleResults(result);
console.log('Process completed successfully.');
logger.info('Accepted', result.filter((r) => r.state === 'ACCEPTED').length, 'cards');
logger.info('Rejected', result.filter((r) => r.state === 'REJECTED').length, 'cards');
} catch (error) {
console.error('An error occurred during the process:', error);
logger.error('An error occurred during the process:', error);
} finally {
logger.info('Process completed.');
}
}

Expand Down
25 changes: 17 additions & 8 deletions src/mailer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ImapFlow, Readable } from 'imapflow';
import log4js from 'log4js';

export interface CardEmail {
title: string;
Expand Down Expand Up @@ -79,11 +80,15 @@ export default class Mailer {
private readonly client: ImapFlow;
private ROOT_PATH: string = process.env['IMAP_ROOT'] || 'API';

private readonly logger = log4js.getLogger('Mailer');

constructor() {
this.logger.level = process.env['LOG_LEVEL'] || 'warn';
this.client = new ImapFlow({
host: process.env['IMAP_HOST'] || 'localhost',
port: Number(process.env['IMAP_PORT'] || '993'),
secure: true,
logger: this.logger,
auth: {
user: process.env['IMAP_USERNAME'] || 'user',
pass: process.env['IMAP_PASSWORD'],
Expand All @@ -102,11 +107,14 @@ export default class Mailer {
const emails: CardEmail[] = [];

try {
for await (const message of this.client.fetch('1:*', {
envelope: true,
headers: true,
flags: true,
})) {
for await (const message of this.client.fetch(
{},
{
envelope: true,
headers: true,
flags: true,
},
)) {
const headers = '' + message.headers;
const plankaBoardId = extractPlankaBoardId(headers);
const plankaListId = extractPlankaListId(headers);
Expand All @@ -130,8 +138,9 @@ export default class Mailer {
for (const email of emails) {
email.body = await downloadEmailBody(this.client, email.uid);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
console.error(e);
// noop
} finally {
lock.release();
}
Expand All @@ -153,10 +162,10 @@ export default class Mailer {
await this.client.mailboxOpen(`${this.ROOT_PATH}/IN`);
for (const result of results) {
if (result.state === 'ACCEPTED') {
console.log('accepted', result.card.uid);
this.logger.trace('accepted', result.card.uid);
await this.acceptEmail(result.card.uid);
} else {
console.log('rejected', result.card.uid);
this.logger.trace('rejected', result.card.uid);
await this.rejectEmail(result.card.uid);
}
}
Expand Down
18 changes: 17 additions & 1 deletion src/planka.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@gewis/planka-client';
import type { List } from '@gewis/planka-client';
import type { Client, Options } from '@hey-api/client-fetch';
import log4js from 'log4js';
import type { CardEmail } from './mailer.ts';

const DEFAULT_PLANKA_URL = process.env['PLANKA_URL'] || 'http://localhost:3000';
Expand All @@ -26,8 +27,11 @@ export default class Planka {

private static boardCache: Map<bigint, CacheEntry | null> = new Map();

private static readonly logger = log4js.getLogger('Planka');

private constructor(settings: { plankaUrl?: string; plankaApiKey?: string }) {
this.settings = settings;
Planka.logger.level = process.env['LOG_LEVEL'] || 'info';
this.initializeClient();
}

Expand Down Expand Up @@ -77,6 +81,7 @@ export default class Planka {
* @param {CardEmail[]} cards - List of CardEmail objects to process.
*/
static async preProcessCards(cards: CardEmail[]) {
Planka.logger.trace('pre processing', cards.length, 'cards');
Planka.pre();
const boardIds = new Set<bigint>();
for (const card of cards) {
Expand All @@ -101,6 +106,7 @@ export default class Planka {

const board = await getBoard({ path: { id: id.toString() } } as Options<GetBoardRequest, false>);
const status = board.response.status;
Planka.logger.trace('caching board', id, 'status', status);

if (status === 200 && board.data) {
// Find the preferred list, which is the list named 'mail' or the first list available
Expand All @@ -114,6 +120,7 @@ export default class Planka {

Planka.boardCache.set(id, { board: board.data, preferredList });
} else if (status >= 400) {
Planka.logger.warn('error caching board', id, 'status', status);
// Mark board as null in case of error
Planka.boardCache.set(id, null);
}
Expand All @@ -138,13 +145,15 @@ export default class Planka {
const board = Planka.boardCache.get(card.boardId);

if (!board) {
Planka.logger.warn('rejecting card', card.uid, 'board not found');
// Reject card if the board is not found in the cache
results.push({ card, state: 'REJECTED' });
continue;
}

const listId = card.listId || board.preferredList?.id;
if (!listId) {
Planka.logger.warn('rejecting card', card.uid, 'list not found');
// Reject card if the board has no list
results.push({ card, state: 'REJECTED' });
continue;
Expand All @@ -160,6 +169,7 @@ export default class Planka {
position: 0,
},
} as Options<CreateCardRequest, false>).then(async (result) => {
Planka.logger.trace('created card', card.uid, 'status', result.response.status);
const cardResult = result.data;
const status = result.response.status;

Expand All @@ -177,7 +187,13 @@ export default class Planka {
description: card.body,
dueDate: card.date ? card.date : null,
},
} as Options<UpdateCardRequest>);
} as Options<UpdateCardRequest>)
.then((result) => {
Planka.logger.trace('updated card', card.uid, 'status', result.response.status);
})
.catch((e) => {
Planka.logger.error('error updating card', card.uid, e);
});
}
});
}
Expand Down
55 changes: 53 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,11 @@ data-view-byte-offset@^1.0.0:
es-errors "^1.3.0"
is-data-view "^1.0.1"

date-format@^4.0.14:
version "4.0.14"
resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400"
integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==

debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
Expand Down Expand Up @@ -1246,7 +1251,7 @@ flat-cache@^4.0.0:
flatted "^3.2.9"
keyv "^4.5.4"

flatted@^3.2.9:
flatted@^3.2.7, flatted@^3.2.9:
version "3.3.1"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
Expand All @@ -1258,6 +1263,15 @@ for-each@^0.3.3:
dependencies:
is-callable "^1.1.3"

fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"

fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
Expand Down Expand Up @@ -1362,7 +1376,7 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"

graceful-fs@^4.2.4:
graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
Expand Down Expand Up @@ -1742,6 +1756,13 @@ jsonc-eslint-parser@^2.0.0:
espree "^9.0.0"
semver "^7.3.5"

jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
optionalDependencies:
graceful-fs "^4.1.6"

"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
version "3.3.5"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
Expand Down Expand Up @@ -1836,6 +1857,17 @@ lodash@^4.17.11, lodash@^4.17.21:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

log4js@^6.9.1:
version "6.9.1"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6"
integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==
dependencies:
date-format "^4.0.14"
debug "^4.3.4"
flatted "^3.2.7"
rfdc "^1.3.0"
streamroller "^3.1.5"

loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
Expand Down Expand Up @@ -2211,6 +2243,11 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==

rfdc@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca"
integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==

run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
Expand Down Expand Up @@ -2336,6 +2373,15 @@ sprintf-js@^1.1.3:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==

streamroller@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff"
integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==
dependencies:
date-format "^4.0.14"
debug "^4.3.4"
fs-extra "^8.1.0"

string.prototype.includes@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92"
Expand Down Expand Up @@ -2577,6 +2623,11 @@ undici-types@~6.19.8:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==

universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==

uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
Expand Down

0 comments on commit f84d4ac

Please sign in to comment.