How can I access file handler to run flush method in deno log module using Typescript

Viewed 344

I need to access the fileHandler object of my logger so I can flush the buffer to the file.

This is my program:

import * as log from "https://deno.land/std@0.75.0/log/mod.ts"
import { Application } from "https://deno.land/x/oak@v6.3.1/mod.ts";

const app   = new Application()
const port  = 7001

await log.setup({
    handlers:{
        file: new log.handlers.FileHandler("DEBUG",{
            filename: "logger.log",
            formatter: lr => {
                return `${lr.datetime.toISOString()} [${lr.levelName}] ${lr.msg}`
            }
        })
    },
    loggers: {
        default: {
            level: "DEBUG",
            handlers: ["file"]
        }
    }
})
const logger = log.getLogger()
logger.debug("hi there")

app.use((ctx) => {
    ctx.response.body = 'Hi there'
})

console.log(`listening on port ${port}`)
app.listen({ port })

My problem is that the log message is never being written to file. If I remove the last line ( app.listen() ) it Does write to the file because the process ends. But if I leave it listening process never ends so the log buffer is never flushed.

If I interrupt the process with Ctrl-C it doesn't write it either

Documentation (https://deno.land/std@0.75.0/log/README.md) says I can force log flush using the flush method from FileHandler. But I don't know how to access the fileHandler object.
So I've tried this:

const logger = log.getLogger()
logger.debug("hi there")
logger.handlers[0].flush()

And it works! but only as javascript, NOT as typescript
As typescript I get this error:

error: TS2339 [ERROR]: Property 'flush' does not exist on type 'BaseHandler'.
logger.handlers[0].flush()
2 Answers

Well, I found a solution.
I just have to import the FileHandler class and cast my handler down from BaseHandler to FileHandler. So I added this line among the imports:

import { FileHandler } from "https://deno.land/std@0.75.0/log/handlers.ts"

And then after creating the logger:

logger.debug("hi there")
const fileHandler = <FileHandler> logger.handlers[0]
fileHandler.flush()

Looks a little weird, I still guess there must be less quirky / more semantic solution for this. But it works ok.

Let us just recap with the help of Santi's answer.

In my experience logs in file work fine in an ending program. I mean a program which dies by itself or with Deno.exit(0). Problem occurs in a never ending loop. In this case logs don't append in their files. Below is how to overcome this situation :

// dev.js : "I want my logs" example
import {serve} from "https://deno.land/std@0.113.0/http/server_legacy.ts";
import * as log from "https://deno.land/std@0.113.0/log/mod.ts";

// very simple setup, adapted from the official standard lib https://deno.land/std@0.113.0/log
await log.setup({
  handlers: {
    file: new log.handlers.FileHandler("WARNING", {
      filename: "./log.txt",
      formatter: "{levelName} {msg}",
    }),
  },
  loggers: {
    default: {
      level: "DEBUG",
      handlers: ["file"],
    },
  },
});

// here we go
let logger;
logger = log.getLogger();
logger.warning('started');  

const fileHandler = logger.handlers[0];
await fileHandler.flush(); // <---- the trick, need to flush ! Thanks Santi

// loop on requests
const srv = serve(`:4321`);
for await (const request of srv) {
  request.respond({body: 'bonjour', status: 200});

  logger.warning('hit !');
  fileHandler.flush();  // <---- flush again
}

Run with

$ deno run -A dev.js

And check the file log.txt with the following trigger

$ curl localhost:4321

This is a very low tech, problably adding important delay to the process. The next level will be to fire a time event to flush every minute or so.

Related