1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 08:39:09 -05:00
denoland-deno/std/log/README.md
2020-06-01 18:31:17 -04:00

241 lines
7.6 KiB
Markdown

# Log
## Usage
```ts
import * as log from "https://deno.land/std/log/mod.ts";
// Simple default logger out of the box. You can customize it
// by overriding logger and handler named "default", or providing
// additional logger configurations
log.debug("Hello world");
log.info("Hello world");
log.warning("Hello world");
log.error("Hello world");
log.critical("500 Internal server error");
// custom configuration with 2 loggers (the default and `tasks` loggers)
await log.setup({
handlers: {
console: new log.handlers.ConsoleHandler("DEBUG"),
file: new log.handlers.FileHandler("WARNING", {
filename: "./log.txt",
// you can change format of output message using any keys in `LogRecord`
formatter: "{levelName} {msg}",
}),
},
loggers: {
// configure default logger available via short-hand methods above
default: {
level: "DEBUG",
handlers: ["console", "file"],
},
tasks: {
level: "ERROR",
handlers: ["console"],
},
},
});
let logger;
// get default logger
logger = log.getLogger();
logger.debug("fizz"); // logs to `console`, because `file` handler requires "WARNING" level
logger.warning("buzz"); // logs to both `console` and `file` handlers
// get custom logger
logger = log.getLogger("tasks");
logger.debug("fizz"); // won't get output because this logger has "ERROR" level
logger.error("buzz"); // log to `console`
// if you try to use a logger that hasn't been configured
// you're good to go, it gets created automatically with level set to 0
// so no message is logged
const unknownLogger = log.getLogger("mystery");
unknownLogger.info("foobar"); // no-op
```
## Advanced usage
### Loggers
Loggers are objects that you interact with. When you use a logger method it
constructs a `LogRecord` and passes it down to its handlers for output. To
create custom loggers, specify them in `loggers` when calling `log.setup`.
#### `LogRecord`
`LogRecord` is an object that encapsulates provided message and arguments as
well some meta data that can be later used when formatting a message.
```ts
class LogRecord {
readonly msg: string;
readonly args: any[];
readonly datetime: Date;
readonly level: number;
readonly levelName: string;
}
```
### Handlers
Handlers are responsible for actual output of log messages. When a handler is
called by a logger, it firstly checks that `LogRecord`'s level is not lower than
level of the handler. If level check passes, handlers formats log record into
string and outputs it to target.
`log` module comes with three built-in handlers:
#### `ConsoleHandler`
This is the default logger. It will output color coded log messages to the
console via `console.log()`. This logger takes `HandlerOptions`:
```typescript
type FormatterFunction = (logRecord: LogRecord) => string;
interface HandlerOptions {
formatter?: string | FormatterFunction; //see `Custom message format` below
}
```
#### `FileHandler`
This handler will output to a file using an optional mode (default is `a`, e.g.
append). The file will grow indefinitely. It uses a buffer for writing to file
and will automatically flush every 30 seconds, though you can trigger this
yourself with `fileHandler.flush()`. Log messages with a log level greater than
error are immediately flushed. This logger takes `FileOptions`:
```typescript
interface FileHandlerOptions {
formatter?: string | FormatterFunction; //see `Custom message format` below
filename: string;
mode?: LogMode; // 'a', 'w', 'x'
}
```
Behavior of the log modes is as follows:
- `'a'` - Default mode. Appends new log messages to the end of an existing log
file, or create a new log file if none exists
- `'w'` - Upon creation of the handler, any existing log file will be removed
and a new one created.
- `'x'` - This will create a new log file and throw an error if one already
exists
This handler requires `--allow-write` permission on the log file.
#### `RotatingFileHandler`
This handler extends the functionality of the `FileHandler` by "rotating" the
log file when it reaches a certain size. `maxBytes` specifies the maximum size
in bytes that the log file can grow to before rolling over to a new one. If the
size of the new log message plus the current log file size exceeds `maxBytes`
then a roll over is triggered. When a roll over occurs, before the log message
is written, the log file is renamed and appended with `.1`. If a `.1` version
already existed, it would have been renamed `.2` first and so on. The maximum
number of log files to keep is specified by `maxBackupCount`. After the renames
are complete the log message is written to the original, now blank, file.
Example: Given `log.txt`, `log.txt.1`, `log.txt.2` and `log.txt.3`, a
`maxBackupCount` of 3 and a new log message which would cause `log.txt` to
exceed `maxBytes`, then `log.txt.2` would be renamed to `log.txt.3` (thereby
discarding the original contents of `log.txt.3` since 3 is the maximum number of
backups to keep), `log.txt.1` would be renamed to `log.txt.2`, `log.txt` would
be renamed to `log.txt.1` and finally `log.txt` would be created from scratch
where the new log message would be written.
This handler uses a buffer for writing to file and will automatically flush
every 30 seconds, though you can trigger this yourself with
`fileHandler.flush()`. Log messages with a log level greater than error are
immediately flushed.
Options for this handler are:
```typescript
interface RotatingFileHandlerOptions {
maxBytes: number;
maxBackupCount: number;
formatter?: string | FormatterFunction; //see `Custom message format` below
filename: string;
mode?: LogMode; // 'a', 'w', 'x'
}
```
Additional notes on `mode` as described above:
- `'a'` Default mode. As above, this will pick up where the logs left off in
rotation, or create a new log file if it doesn't exist.
- `'w'` in addition to starting with a clean `filename`, this mode will also
cause any existing backups (up to `maxBackupCount`) to be deleted on setup
giving a fully clean slate.
- `'x'` requires that neither `filename`, nor any backups (up to
`maxBackupCount`), exist before setup
This handler requires both `--allow-read` and `--allow-write` permissions on the
log files.
### Custom message format
If you want to override default format of message you can define `formatter`
option for handler. It can be either simple string-based format that uses
`LogRecord` fields or more complicated function-based one that takes `LogRecord`
as argument and outputs string.
Eg.
```ts
await log.setup({
handlers: {
stringFmt: new log.handlers.ConsoleHandler("DEBUG", {
formatter: "[{levelName}] {msg}"
}),
functionFmt: new log.handlers.ConsoleHandler("DEBUG", {
formatter: logRecord => {
let msg = `${logRecord.level} ${logRecord.msg}`;
logRecord.args.forEach((arg, index) => {
msg += `, arg${index}: ${arg}`;
});
return msg;
}
}),
},
loggers: {
default: {
level: "DEBUG",
handlers: ["stringFmt", "functionFmt"],
},
}
})
// calling
log.debug("Hello, world!", 1, "two", [3, 4, 5]);
// results in:
[DEBUG] Hello, world! // output from "stringFmt" handler
10 Hello, world!, arg0: 1, arg1: two, arg3: [3, 4, 5] // output from "functionFmt" formatter
```
#### Custom handlers
Custom handlers can be implemented by subclassing `BaseHandler` or
`WriterHandler`.
`BaseHandler` is bare-bones handler that has no output logic at all,
`WriterHandler` is an abstract class that supports any target with `Writer`
interface.
During setup async hooks `setup` and `destroy` are called, you can use them to
open and close file/HTTP connection or any other action you might need.
For examples check source code of `FileHandler` and `TestHandler`.